diff --git a/src/machine/m_xt_ibm5550.c b/src/machine/m_xt_ibm5550.c index e1c842ecb..fed353344 100644 --- a/src/machine/m_xt_ibm5550.c +++ b/src/machine/m_xt_ibm5550.c @@ -57,6 +57,7 @@ #include <86box/plat_unused.h> #define EMU_DEVICE_H #include <86box/pit.h> +#include <86box/mouse.h> // #define EPOCH_FONTROM_SIZE (1024 * 1024) // #define EPOCH_FONTROM_MASK 0xffff @@ -159,7 +160,8 @@ enum epoch_nvr_ADDR { #endif #ifdef ENABLE_EPOCH_LOG -// # define ENABLE_EPOCH_DEBUGIO 1 +// #define ENABLE_EPOCH_DEBUGIO 1 +// #define ENABLE_EPOCH_DEBUGKBD 1 int epoch_do_log = ENABLE_EPOCH_LOG; static void @@ -181,6 +183,12 @@ epoch_log(const char *fmt, ...) #else # define epoch_iolog(fmt, ...) #endif +#ifdef ENABLE_EPOCH_DEBUGKBD +# define epoch_kbdlog epoch_log +#else +# define epoch_kbdlog(fmt, ...) +#endif + typedef struct epoch_t { mem_mapping_t cmapping; @@ -332,7 +340,7 @@ epoch_out(uint16_t addr, uint16_t val, void *priv) case LC_START_ADDRESS_HIGH: case LC_CURSOR_LOC_HIGH: case LC_LIGHT_PEN_HIGH: - val &= 0x3F; + val &= 0x3F; /* this is required for the IPL BAT test */ break; } epoch->crtc[epoch->crtcaddr] = val; @@ -428,7 +436,7 @@ epoch_out(uint16_t addr, uint16_t val, void *priv) // epoch->attrc[epoch->attraddr & 0x3f] = val; // break; default: - epoch_iolog("epoch? Out addr %03X val %02X\n", addr, val); + // epoch_iolog("epoch? Out addr %03X val %02X\n", addr, val); break; } } @@ -482,8 +490,8 @@ epoch_in(uint16_t addr, void *priv) if(epoch->cgastat & 8) temp &= 0x7f; } - temp &= 0xfe;/* equipment ? color or monochrome monitor */ - // temp |= 0x01; + temp |= 0x01; /* monitor mono or !color */ + // temp &= 0xfe; /* color */ break; } if (addr != 0x3DA) @@ -1117,14 +1125,14 @@ static void epoch_vram_writeb(uint32_t addr, uint8_t val, void *priv) { epoch_t *epoch = (epoch_t *) priv; - // epoch_log("epoch_vram_writeb: Write to %x, val %x\n", addr, val); + // epoch_log("%04X:%04X epoch_vram_writeb: %x, val %x\n", cs >> 4, cpu_state.pc, addr, val); cycles -= video_timing_write_b; epoch_vram_write(addr, val, epoch); } static void epoch_vram_writew(uint32_t addr, uint16_t val, void *priv) { - // epoch_log("epoch_vram_writ ew: Write to %x, val %x\n", addr, val); + // epoch_log("%04X:%04X epoch_vram_writew: %x, val %x\n", cs >> 4, cpu_state.pc, addr, val); epoch_t *epoch = (epoch_t *) priv; cycles -= video_timing_write_w; epoch_vram_write(addr, val & 0xff, epoch); @@ -1311,23 +1319,23 @@ xxxx xxx1: Memory or parity error? break; /* I/O A2h R: -xxxx 0x0x: Color CRT +xxxx 0x0x: Color 16 CRT xxxx 1x0x: Mono 24 CRT ? -xxxx xx1x: 16 pixel CRT +xxxx xx1x: Mono 16 CRT xx1x xxxx: No hard drive x1xx xxxx: No floppy drive 1xxx xxxx: No (bootable?) hard drive */ case 0xA2: ret = 0xA8;/* Mono 24 */ - // ret = 0xA8;/* Mono 16 */ + // ret = 0xA2;/* Mono 16 */ break; /* I/O A3h R: -xxxx x001: Main RAM 256 KB? -xxxx x011: Main RAM 384 KB? -xxxx x111: Main RAM 512 KB? -xxxx x000: Main RAM 640 KB? +xxxx x111: Main RAM 256 KB +xxxx x110: Main RAM 384 KB +xxxx x100: Main RAM 512 KB +xxxx x000: Main RAM 640 KB xxxx 1xxx: Serial port 3f8h xxx1 xxxx: Serial port 2f8h */ @@ -1417,14 +1425,18 @@ typedef struct epochkbd_t { uint8_t pa; uint8_t pb; - uint8_t clock; + uint8_t clk_hold; uint8_t key_waiting; - uint8_t reset_step; + uint8_t kbd_reset_step; + uint8_t mouse_reset_step; + int mouse_enabled; + int mouse_queue_num; + uint8_t mouse_queue[4]; pc_timer_t send_delay_timer; } epochkbd_t; -static uint8_t key_queue[16]; +static uint8_t key_queue[16]; /* buffer in the keyboard */ static int key_queue_start = 0; static int key_queue_end = 0; @@ -1435,24 +1447,33 @@ kbd_epoch_poll(void *priv) timer_advance_u64(&kbd->send_delay_timer, 1000 * TIMER_USEC); - if (kbd->pb & 0x04) + if (kbd->pb & 0x04) /* controller is sending something to keyboard */ + return; + + if (!(kbd->pb & 0x08)) /* keyboard interrupt is disabled */ return; if (kbd->want_irq) { kbd->want_irq = 0; kbd->pa = kbd->key_waiting; - kbd->pb &= 0xF7; - kbd->blocked = 1; + kbd->blocked = 1; picint(EPOCH_IRQ3_BIT); - epoch_log("epochkbd: kbd_poll(): keyboard_xt : take IRQ\n"); + epoch_kbdlog("epochkbd: kbd_poll(): keyboard_xt : take IRQ\n"); } - if ((key_queue_start != key_queue_end) && !kbd->blocked) { - kbd->key_waiting = key_queue[key_queue_start]; - epoch_log("epochkbd: reading %02X from the key queue at %i\n", - kbd->key_waiting, key_queue_start); - key_queue_start = (key_queue_start + 1) & 0x0f; - kbd->want_irq = 1; + if (!kbd->blocked) { + if (kbd->mouse_queue_num > 0) { + kbd->mouse_queue_num--; + kbd->key_waiting = kbd->mouse_queue[kbd->mouse_queue_num]; + epoch_kbdlog("epochkbd: reading %02X from the mouse queue at %i\n", kbd->key_waiting, kbd->mouse_queue_num); + kbd->want_irq = 1; + } else if (key_queue_start != key_queue_end) { + kbd->key_waiting = key_queue[key_queue_start]; + epoch_kbdlog("epochkbd: reading %02X from the key queue at %i\n", + kbd->key_waiting, key_queue_start); + key_queue_start = (key_queue_start + 1) & 0x0f; + kbd->want_irq = 1; + } } } @@ -1481,7 +1502,7 @@ static void kbd_epoch_adddata(uint16_t val) { key_queue[key_queue_end] = val; - epoch_log("XTkbd: %02X added to key queue at %i\n", + epoch_kbdlog("epochkbd: %02X added to key queue at %i\n", val, key_queue_end); key_queue_end = (key_queue_end + 1) & 0x0f; } @@ -1493,38 +1514,58 @@ kbd_adddata_ex(uint16_t val) kbd_epoch_adddata_process(val, kbd_epoch_adddata); } +/* +I/O 61h W: +xxxx xxx1: ? (used by kbd interrupt in DOS) +xxxx xx1x: Beep +xxxx x1xx: Hold clock line low (used to reset kbd) +xxxx 1xxx: Enable kbd? +xxx1 xxxx: Send data from system to kbd +1xxx xxxx: Clear buffer? +*/ static void kbd_write(uint16_t port, uint8_t val, void *priv) { epochkbd_t *kbd = (epochkbd_t *) priv; - uint8_t new_clock; - epoch_log("%04X:%04X epochkbd: Port %04X out: %02X\n", cs >> 4, cpu_state.pc, port, val); + uint8_t new_clock; + epoch_kbdlog("%04X:%04X epochkbd: Port %02X out: %02X BX: %04x", cs >> 4, cpu_state.pc, port, val, BX); + epoch_kbdlog(" Clk: %x %x\n", kbd->blocked, kbd->clk_hold); switch (port) { + case 0x60: /* Diagnostic Output */ + if ((kbd->pb & 0x10) && ((val & 0xf0) == 0xd0)) { + kbd->mouse_enabled = 1; + epoch_kbdlog("epochkbd: Mouse is enabled.\n"); + } + break; case 0x61: /* Keyboard Control Register (aka Port B) */ - if (val & 0x08) { + kbd->pb = val; + + if ((val & 0x18) == 0x08) { new_clock = !(val & 0x04); - if (kbd->clock && new_clock) { + /* Trigger kbd reset after the clk line is reset to a high level */ + if (kbd->clk_hold && new_clock) { key_queue_start = key_queue_end = 0; kbd->want_irq = 0; kbd->blocked = 0; - kbd->reset_step = 0; - /* Specific 5556 keyboards send three bytes of identification code, - but this simply sends AAh that can pass the IPL and DOS K3.4 init. */ - kbd_epoch_adddata(0xaa); - } else if (!(kbd->pb & 0x08)) { - kbd->pa = 0; - kbd->blocked = 0; + kbd->kbd_reset_step = 0; + kbd->clk_hold = 0; + kbd->mouse_enabled = 0; + kbd->mouse_queue_num = 0; + epoch_kbdlog("epochkbd: Starting keyboard reset sequence.\n"); + } + } else if ((val & 0x18) == 0x18) { + new_clock = !(val & 0x04); + if (kbd->clk_hold && new_clock) { + kbd->mouse_reset_step = 0; + epoch_kbdlog("epochkbd: Starting mouse reset sequence.\n"); } } - kbd->pb = val; if (kbd->pb & 0x08) - kbd->clock = !!(kbd->pb & 0x04); - ppi.pb = val; + kbd->clk_hold = !!(kbd->pb & 0x04); timer_process(); - speaker_update(); speaker_gated = val & 2; @@ -1534,18 +1575,31 @@ kbd_write(uint16_t port, uint8_t val, void *priv) was_speaker_enable = 1; pit_devs[0].set_gate(pit_devs[0].data, TIMER_CTR_2, val & 2); - break; + if (val & 0x80) { + /* clear buffer */ + kbd->pa = 0; + kbd->blocked = 0; + /* IRQ will be cleared by the software */ + // picintc(EPOCH_IRQ3_BIT); + } + break; default: break; } } +/* +I/O 61h R +xxxx xx1x: Beep input? +xxxx x1xx: KB -CLK? +xxxx 1xxx: KB -DATA? +*/ static uint8_t kbd_read(uint16_t port, void *priv) { epochkbd_t *kbd = (epochkbd_t *) priv; - uint8_t ret = 0xff; + uint8_t ret = 0; switch (port) { case 0x60: /* Keyboard Data Register (aka Port A) */ @@ -1553,36 +1607,50 @@ kbd_read(uint16_t port, void *priv) break; case 0x61: /* Keyboard Control Register (aka Port B) */ - /* Bit 3 and 2: Keyboard Data and Clk line ? */ - if (kbd->reset_step < 18) { + ret = kbd->pb & 0xf0; /* reset sense bit */ + ret |= 0x0c; /* reset sense bit */ + if (kbd->kbd_reset_step < 18) { ret &= 0xf3; - if (kbd->reset_step & 1) + if (!!(kbd->kbd_reset_step & 1)) ret |= 0x04; - switch (kbd->reset_step) { /* AAh (1010 1010) in serial data */ - case 3: - case 7: - case 11: - case 15: - ret |= 0x00; - break; - default: + switch (kbd->kbd_reset_step >> 1) { /* AAh (0 1010 1010) in serial data */ + case 0: + case 2: + case 4: + case 6: + case 8: ret |= 0x08; break; + default: + ret |= 0x00; + break; } - kbd->reset_step += 1; + epoch_kbdlog(" reset step: %d %x %x", kbd->kbd_reset_step, ret & 0x08, ret & 0x04); + epoch_kbdlog(" Clk: %x %x\n", kbd->blocked, kbd->clk_hold); + kbd->kbd_reset_step++; + /* Specific 5556 keyboards send three bytes of identification code, + but this simply sends AAh that can pass the IPL and DOS K3.4 init. */ + if (kbd->kbd_reset_step == 18) + kbd_epoch_adddata(0xaa); + } else if (kbd->mouse_reset_step < 20) { + ret &= 0xf3; + if (!!(kbd->mouse_reset_step & 1)) + ret |= 0x04; + epoch_kbdlog(" reset step: %d %x %x\n", kbd->mouse_reset_step, ret & 0x08, ret & 0x04); + kbd->mouse_reset_step++; } + /* Bit 1: Timer 2 (Speaker) out state */ - if (pit_devs[0].get_outlevel(pit_devs[0].data, TIMER_CTR_2) && speaker_enable) - ret &= 0xfd;/* 1111 1101 */ + if (pit_devs[0].get_outlevel(pit_devs[0].data, TIMER_CTR_2) && speaker_enable) + ret &= 0xfd; /* 1111 1101 */ else ret |= 0x02; - break; + break; default: break; } - // epoch_log("%04X:%04X epochkbd: Port %04X in : %02X BX:%04x\n", cs >> 4, cpu_state.pc, port, ret, BX); - + epoch_kbdlog("%04X:%04X epochkbd: Port %02X in : %02X pb: %02x CX: %04x\n", cs >> 4, cpu_state.pc, port, ret, kbd->pb, CX); return ret; } @@ -1595,11 +1663,17 @@ kbd_reset(void *priv) kbd->blocked = 0; kbd->pa = 0x00; kbd->pb = 0x00; + kbd->kbd_reset_step = 0xff; + kbd->mouse_reset_step = 0xff; keyboard_scan = 1; key_queue_start = 0; key_queue_end = 0; + + kbd->mouse_enabled = 0; + kbd->mouse_queue_num = 0; + kbd_epoch_adddata(0xaa); } static void * @@ -1614,8 +1688,6 @@ kbd_init(const device_t *info) keyboard_send = kbd_adddata_ex; kbd_reset(kbd); - key_queue_start = key_queue_end = 0; - timer_add(&kbd->send_delay_timer, kbd_epoch_poll, kbd, 1); keyboard_set_table(scancode_set8a); @@ -1634,7 +1706,6 @@ kbd_close(void *priv) /* Disable scanning. */ keyboard_scan = 0; - keyboard_send = NULL; io_removehandler(0x0060, 2, @@ -1657,6 +1728,71 @@ static const device_t kbc_epoch_device = { .config = NULL }; +/* +The IBM 5550 DOS K3.44 comes with a mouse driver +for the M1 and P1 keyboards that have a built-in mouse adapter. +The mouse adapter for the expansion slot was also available, +but it may require the bundled driver. (not confirmed) + +IBM 5550 DOS K3.4 Mouse Driver (Int 0Bh and 33h) +61h <- 1st kbd indata +[si+4Ch] status <- 2nd kbd indata +1xxx xxxx negative dX +x1xx xxxx bit 9 of dX +xx1x xxxx negative dY +xxx1 xxxx bit 9 of dY +xxxx x1xx cursor moved +xxxx xx1x right button +xxxx xxx1 left button +[si+4Dh] previous button status <- [si+4Ch] +[si+4Eh] dX <- 3rd kbd indata +[si+50h] dY <- 4th kbd indata +*/ +static int +epoch_mouse_poll(void *priv) +{ + epochkbd_t *kbd = (epochkbd_t *) priv; + int dat = mouse_get_buttons_ex(); + int dx, dy; + + if (!kbd->mouse_enabled) + return 0; + + if (kbd->mouse_queue_num > 0) + return 0; + + if (kbd->pb & 0x10) + return 0; + + dat &= 0x03; + if (!mouse_moved()) { + kbd->mouse_queue[1] = 0x61; + kbd->mouse_queue[0] = dat; + kbd->mouse_queue_num = 2; + } else { + dat |= 0x04; + mouse_subtract_x(&dx, NULL, -512, 511, 0); + mouse_clear_x(); + if (dx < 0) + dat |= 0x20; + if (dx & 0x100) + dat |= 0x10; + mouse_subtract_y(&dy, NULL, -512, 511, 0, 0); + mouse_clear_y(); + if (dy < 0) + dat |= 0x80; + if (dy & 0x100) + dat |= 0x40; + epoch_log("Mouse moved %x %x %x\n", dat, dx, dy); + kbd->mouse_queue[3] = 0x61; + kbd->mouse_queue[2] = dat; + kbd->mouse_queue[1] = dx & 0xff; + kbd->mouse_queue[0] = dy & 0xff; + kbd->mouse_queue_num = 4; + } + return 0; +} + static void epoch_nvr_time_set(uint8_t *regs, struct tm *tm) { @@ -1940,7 +2076,7 @@ epoch_init(UNUSED(const device_t *info)) epoch->vram = calloc(1, 256* 1024); epoch->cram = calloc(1, 4 * 1024); // epoch->fontcard.rom = calloc(1, EPOCH_FONTROM_SIZE); - //epoch_video_load_font("roms/machines/ibm5550/GEN1FONT.BIN", epoch); + // epoch_video_load_font("roms/machines/ibm5550/GEN1FONT.BIN", epoch); epoch->epochconst = (uint64_t) ((cpuclock / EPOCH_PIXELCLOCK) * (double) (1ull << 32)); @@ -1963,7 +2099,7 @@ epoch_init(UNUSED(const device_t *info)) io_sethandler(0x44, 0x0001, epoch_misc_in, NULL, NULL, epoch_misc_out, NULL, NULL, epoch); - io_sethandler(0xA0, 0x0005, + io_sethandler(0xA0, 0x0006, epoch_misc_in, NULL, NULL, epoch_misc_out, NULL, NULL, epoch); io_sethandler(0x310, 0x0008, epoch_misc_in, NULL, NULL, epoch_misc_out, NULL, NULL, epoch); @@ -2051,7 +2187,7 @@ epoch_force_redraw(void *priv) } static const device_t epoch_device = { - .name = "IBM 5550 Video Controller (Epoch)", + .name = "IBM 5550 (Epoch) Video Controller", .internal_name = "ibm5550vid", .flags = DEVICE_ISA, .local = 0, @@ -2115,7 +2251,7 @@ machine_xt_ibm5550_init(const machine_t *model) device_add(&fdc_xt_5550_device); - device_add(&kbc_epoch_device); + epochkbd_t *kbc = device_add(&kbc_epoch_device); pic_init(); dma_init(); @@ -2128,5 +2264,12 @@ machine_xt_ibm5550_init(const machine_t *model) serial_t *uart = device_add(&ns8250_device); serial_setup(uart, 0x3f8, 1);/* Use IRQ 1 */ + if (mouse_type == MOUSE_TYPE_INTERNAL) { + /* Tell mouse driver about our internal mouse. */ + mouse_reset(); + mouse_set_buttons(2); + mouse_set_poll(epoch_mouse_poll, kbc); + } + return ret; } diff --git a/src/machine/machine_table.c b/src/machine/machine_table.c index 66d119520..85b09676e 100644 --- a/src/machine/machine_table.c +++ b/src/machine/machine_table.c @@ -2710,7 +2710,7 @@ const machine_t machines[] = { .max_multi = 0 }, .bus_flags = MACHINE_PC, - .flags = MACHINE_VIDEO_FIXED | MACHINE_KEYBOARD, + .flags = MACHINE_VIDEO_FIXED | MACHINE_FDC | MACHINE_KEYBOARD | MACHINE_MOUSE, .ram = { .min = 256, .max = 640,