diff options
Diffstat (limited to 'mousetsr.c')
| -rw-r--r-- | mousetsr.c | 625 |
1 files changed, 491 insertions, 134 deletions
@@ -27,6 +27,9 @@ #include "int16kbd.h" #include "int2fwin.h" #include "int33.h" +#include "pic.h" +#include "serial.h" +#include "sermouse.h" #include "vbox.h" #include "vmware.h" #include "mousetsr.h" @@ -35,6 +38,8 @@ TSRDATA data; +static const char version_string[] = "VBADOS"; + static const uint16_t default_cursor_graphic[] = { 0x3FFF, 0x1FFF, 0x0FFF, 0x07FF, 0x03FF, 0x01FF, 0x00FF, 0x007F, @@ -537,6 +542,8 @@ static void refresh_video_info(void) reload_video_info(); + // This is one of these compatibility-delicate things + // TODO: Investigate correct behavior here if (data.video_mode.type != VIDEO_UNKNOWN) { // If we know the screen size for this mode, then reset the window to it data.min.x = 0; @@ -547,43 +554,52 @@ static void refresh_video_info(void) } } -/** Calls the application-registered event handler. */ -static void call_event_handler(void (__far *handler)(), uint16_t events, - uint16_t buttons, int16_t x, int16_t y, - int16_t delta_x, int16_t delta_y) +/** Obtains INT33_EVENT_MASK bitmask corresponding to btn. */ +static uint16_t button_event_mask(int btn, bool released) { -#if TRACE_EVENTS - dprintf("calling event handler events=0x%x buttons=0x%x x=%d y=%d dx=%d dy=%d\n", - events, buttons, x, y, delta_x, delta_y); -#endif - - __asm { - mov ax, [events] - mov bx, [buttons] - mov cx, [x] - mov dx, [y] - mov si, [delta_x] - mov di, [delta_y] - - call dword ptr [handler] + unsigned int idx; + if (btn >= 3) { + idx = INT33_EVENT_MASK_4TH_BUTTON_PRESSED_INDEX; + btn -= 3; + } else { + idx = INT33_EVENT_MASK_LEFT_BUTTON_PRESSED_INDEX; } + idx += (btn * 2); + if (released) idx++; + return 1 << idx; } +/** Calls the application-registered event handler. */ +static void call_event_handler(uint16_t events, uint16_t buttons, + int16_t x, int16_t y, + int16_t delta_x, int16_t delta_y, + void (__far *handler)()); +#pragma aux call_event_handler = \ + "push bp" \ + "mov bp, sp" \ + "push ds" \ + "call dword ptr 0x2[bp]" \ + "pop ds" \ + "pop bp" \ + __parm __caller [ax] [bx] [cx] [dx] [si] [di] [] \ + __modify [es fs gs] + /** Process a mouse event internally. * @param buttons currently pressed buttons as a bitfield * @param absolute whether mouse coordinates are an absolute value * @param x y if absolute, then absolute coordinates in screen pixels * if relative, then relative coordinates in mickeys - * @param z relative wheel mouse movement + * @param wheeln wheel number (0 = vertical, 1 = horizontal) + * @param z delta movement reported for that wheel (or 0) */ -static void handle_mouse_event(uint16_t buttons, bool absolute, int x, int y, int z) +static void handle_mouse_event(uint16_t buttons, bool absolute, int x, int y, char wheeln, int z) { uint16_t events = 0; int i; #if TRACE_EVENTS - dprintf("handle mouse event %s buttons=0x%x x=%d y=%d z=%d\n", - absolute ? "absolute" : "relative", buttons, x, y, z); + dprintf("handle mouse event %s buttons=0x%hx x=%d y=%d z%d=%d\n", + absolute ? "absolute" : "relative", buttons, x, y, wheeln, z); #endif if (absolute) { @@ -647,43 +663,44 @@ static void handle_mouse_event(uint16_t buttons, bool absolute, int x, int y, in bound_position_to_window(); #if USE_WHEEL - if (data.haswheel && z) { - if (!data.usewheelapi && (data.wheel_up_key || data.wheel_down_key)) { + if (data.num_wheels && z) { + if (!data.usewheelapi && (data.wheel_key[wheeln][WHEEL_DIR_UP] || data.wheel_key[wheeln][WHEEL_DIR_DOWN])) { // Emulate keystrokes on wheel movement - if (z < 0 && data.wheel_up_key) { + if (z < 0 && data.wheel_key[wheeln][WHEEL_DIR_UP]) { for (; z < 0; z++) { - int16_store_keystroke(data.wheel_up_key); + int16_store_keystroke(data.wheel_key[wheeln][WHEEL_DIR_UP], 0); } - } else if (z > 0 && data.wheel_down_key) { + } else if (z > 0 && data.wheel_key[wheeln][WHEEL_DIR_DOWN]) { for (; z > 0; z--) { - int16_store_keystroke(data.wheel_down_key); + int16_store_keystroke(data.wheel_key[wheeln][WHEEL_DIR_DOWN], 0); } } } else { - events |= INT33_EVENT_MASK_WHEEL_MOVEMENT; + if (wheeln == 1) events |= INT33_EVENT_MASK_HORIZ_WHEEL_MOVEMENT; + else events |= INT33_EVENT_MASK_WHEEL_MOVEMENT; // Higher byte of buttons contains wheel movement buttons |= (z & 0xFF) << 8; // Accumulate delta wheel movement - data.wheel_delta += z; - data.wheel_last.x = data.pos.x; - data.wheel_last.y = data.pos.y; + data.wheel[wheeln].delta += z; + data.wheel[wheeln].last.x = data.pos.x; + data.wheel[wheeln].last.y = data.pos.y; } } #endif // Update button status - for (i = 0; i < NUM_BUTTONS; ++i) { + for (i = 0; i < data.num_buttons; ++i) { uint8_t btn = 1 << i; - uint8_t evt = 0; + uint16_t evt = 0; if ((buttons & btn) && !(data.buttons & btn)) { // Button pressed - evt = 1 << (1 + (i * 2)); // Press event mask + evt = button_event_mask(i, false); data.button[i].pressed.count++; data.button[i].pressed.last.x = data.pos.x; data.button[i].pressed.last.y = data.pos.y; } else if (!(buttons & btn) && (data.buttons & btn)) { // Button released - evt = 1 << (2 + (i * 2)); // Release event mask + evt = button_event_mask(i, true); data.button[i].released.count++; data.button[i].released.last.x = data.pos.x; data.button[i].released.last.y = data.pos.y; @@ -694,41 +711,90 @@ static void handle_mouse_event(uint16_t buttons, bool absolute, int x, int y, in refresh_cursor(); - events &= data.event_mask; - if (data.event_handler && events) { - x = snap_to_grid(data.pos.x, data.screen_granularity.x); - y = snap_to_grid(data.pos.y, data.screen_granularity.y); + if (!data.event_handler) { + // No event handler + return; + } - call_event_handler(data.event_handler, events, - buttons, x, y, data.delta.x, data.delta.y); + events &= data.event_mask; + if (!(events & ~INT33_EVENT_MASK_ABSOLUTE)) { + // No event passes the mask + return; } + + x = snap_to_grid(data.pos.x, data.screen_granularity.x); + y = snap_to_grid(data.pos.y, data.screen_granularity.y); + +#if TRACE_EVENTS + dprintf("calling user event handler events=0x%x buttons=0x%x x=%d y=%d dx=%d dy=%d\n", + events, buttons, x, y, data.delta.x, data.delta.y); +#endif + + call_event_handler(events, buttons, x, y, data.delta.x, data.delta.y, + data.event_handler); + +#if TRACE_EVENTS + dputs("return from user event handler"); +#endif } static void handle_ps2_packet(void) { - unsigned status; - int x, y, z = 0; - bool abs = false; + unsigned status = data.cur_packet[0]; - // Decode the PS2 packet... - status = data.ps2_packet[0]; - x = data.ps2_packet[1]; - y = data.ps2_packet[2]; + // Decode basic PS/2 packet + unsigned buttons = status & (PS2M_STATUS_BUTTON_1 | PS2M_STATUS_BUTTON_2 | PS2M_STATUS_BUTTON_3); + int x = data.cur_packet[1], y = data.cur_packet[2]; -#if USE_WHEEL - if (data.haswheel) { - // Sign-extend Z - z = (int8_t) data.ps2_packet[3]; - } -#endif + // For the extended byte... + bool abs = false; + int z = 0; + char wheeln = 0; // Sign-extend X, Y as per the status byte x = status & PS2M_STATUS_X_NEG ? 0xFF00 | x : x; y = -(status & PS2M_STATUS_Y_NEG ? 0xFF00 | y : y); + // Decode extended byte +#if USE_WHEEL + switch (data.device_id) { + case PS2M_DEVICE_ID_IMPS2: + // ImPS/2. The fourth packet is the (vertical) wheel movement. + z = (int8_t) data.cur_packet[3]; + break; +#if USE_IMEX + case PS2M_DEVICE_ID_IMEX: + case PS2M_DEVICE_ID_IMEX_HORZ: + // IntelliMouse Explorer. It can report either: + if (data.cur_packet[3] & PS2M_IMEX_VERTICAL_SCROLL) { + // Vertical scrolling, with 6 bits of precision. + z = sign_extend(data.cur_packet[3], 6); + // Assume 4th/5th buttons are still pressed if they were + buttons |= data.buttons & (INT33_BUTTON_MASK_4TH|INT33_BUTTON_MASK_5TH); + } else if (data.cur_packet[3] & PS2M_IMEX_HORIZONTAL_SCROLL) { + // Horizontal scrolling, with 6 bits of precision. + z = sign_extend(data.cur_packet[3], 6); + wheeln = 1; + buttons |= data.buttons & (INT33_BUTTON_MASK_4TH|INT33_BUTTON_MASK_5TH); + } else { + // Or 2 extra buttons (4, 5) + if (data.cur_packet[3] & PS2M_IMEX_BUTTON_4) { + buttons |= INT33_BUTTON_MASK_4TH; + } + if (data.cur_packet[3] & PS2M_IMEX_BUTTON_5) { + buttons |= INT33_BUTTON_MASK_5TH; + } + // Plus (vertical) scrolling with 4 bits of precision. + z = sign_extend(data.cur_packet[3], 4); + } + break; +#endif /* USE_IMEX */ + } +#endif /* USE_WHEEL */ + #if TRACE_PROTO - dprintf("ps2 packet %x %d %d %d\n", status, x, y, z); -#endif /* TRACE_EVENTS */ + dprintf("ps2 decoded packet buttons=%x x=%d y=%d z%d=%d\n", buttons, x, y, wheeln, z); +#endif /* TRACE_PROTO */ #if USE_VIRTUALBOX if (data.vbavail) { @@ -781,16 +847,17 @@ static void handle_ps2_packet(void) y = scaleu(vmw.y & 0xFFFFU, 0xFFFFU, MAX(data.max.y, data.screen_max.y)); z = (uint8_t) vmw.z; + wheeln = 0; // VMware only supports wheel 0 } if (vmw.status & VMWARE_ABSPOINTER_STATUS_BUTTON_LEFT) { - status |= PS2M_STATUS_BUTTON_1; + buttons |= INT33_BUTTON_MASK_LEFT; } if (vmw.status & VMWARE_ABSPOINTER_STATUS_BUTTON_RIGHT) { - status |= PS2M_STATUS_BUTTON_2; + buttons |= INT33_BUTTON_MASK_RIGHT; } if (vmw.status & VMWARE_ABSPOINTER_STATUS_BUTTON_MIDDLE) { - status |= PS2M_STATUS_BUTTON_3; + buttons |= INT33_BUTTON_MASK_CENTER; } } else { return; // Ignore the PS/2 packet otherwise, it is likely garbage @@ -798,8 +865,7 @@ static void handle_ps2_packet(void) } #endif /* USE_VMWARE */ - handle_mouse_event(status & (PS2M_STATUS_BUTTON_1 | PS2M_STATUS_BUTTON_2 | PS2M_STATUS_BUTTON_3), - abs, x, y, z); + handle_mouse_event(buttons, abs, x, y, wheeln, z); } /** PS/2 BIOS calls this routine to notify mouse events. @@ -811,11 +877,11 @@ static void ps2_mouse_handler(uint16_t word0, uint16_t word1, uint16_t word2, ui uint16_t ticks = bda_get_tick_count_lo(); // Are we using the BIOS in 3-packet mode directly? - if (data.bios_packet_size == PS2M_PACKET_SIZE_PLAIN) { + if (data.bios_packet_size == PS2M_PACKET_SIZE_STD) { // Just forward it to the full packet handler. - data.ps2_packet[0] = word0; - data.ps2_packet[1] = word1; - data.ps2_packet[2] = word2; + data.cur_packet[0] = word0; + data.cur_packet[1] = word1; + data.cur_packet[2] = word2; (void) word3; handle_ps2_packet(); return; @@ -825,23 +891,23 @@ static void ps2_mouse_handler(uint16_t word0, uint16_t word1, uint16_t word2, ui // receiving one byte at a time. // We have to compute synchronization ourselves. -#if TRACE_PROTO - dprintf("ps2 callback byte %d/%d = %x\n", - 1 + data.cur_packet_bytes, data.packet_size, word0 & 0xFF); -#endif /* TRACE_EVENTS */ - if (data.cur_packet_bytes && - ticks >= data.cur_packet_ticks + MAX_PS2_PACKET_DELAY) { + ticks >= data.cur_packet_ticks + MAX_PACKET_DELAY) { // Assume the start of a new packet - dprintf("dropping packet! prev_ticks=%u new_ticks=%u\n", - data.cur_packet_ticks, ticks); + dprintf("dropping packet! cur_bytes=%u prev_ticks=%u new_ticks=%u\n", + data.cur_packet_bytes, data.cur_packet_ticks, ticks); data.cur_packet_bytes = 0; } if (data.cur_packet_bytes == 0) { data.cur_packet_ticks = ticks; } - data.ps2_packet[data.cur_packet_bytes] = word0; +#if TRACE_PROTO + dprintf("ps2 callback byte %d/%d = %hx\n", + 1 + data.cur_packet_bytes, data.packet_size, word0 & 0xFF); +#endif /* TRACE_PROTO */ + + data.cur_packet[data.cur_packet_bytes] = word0; data.cur_packet_bytes++; if (data.cur_packet_bytes >= data.packet_size) { @@ -922,28 +988,41 @@ static void set_absolute(bool enable) } #endif /* USE_INTEGRATION */ -static void reset_mouse_hardware() +static bool reset_ps2_mouse() { int err; // Stop receiving bytes... ps2m_enable(false); + data.port = 0; data.bios_packet_size = PS2M_PACKET_SIZE_STREAMING; // Default to use the BIOS in streaming mode - data.packet_size = PS2M_PACKET_SIZE_PLAIN; + data.packet_size = PS2M_PACKET_SIZE_STD; data.cur_packet_bytes = 0; data.cur_packet_ticks = 0; + data.num_buttons = MIN(3, MAX_BUTTONS); +#if USE_WHEEL + data.num_wheels = 0; + data.usewheelapi = 0; +#endif + + err = ps2m_get_device_id(&data.device_id); + if (err) data.device_id = PS2M_DEVICE_ID_STD; #if USE_WIN386 if (data.haswin386) { uint8_t device_id; - // Normally, win386 does not support anything except PS2M_PACKET_SIZE_PLAIN - // However, if we detect our special wheelvkd driver is running... - err = ps2m_get_device_id(&device_id); - if (err || device_id != PS2M_DEVICE_ID_IMPS2) { - // Our special driver is not running... - dputs("Windows running, using plain packet size"); - data.bios_packet_size = PS2M_PACKET_SIZE_PLAIN; + // Normally, win386 does not support anything except standard mouse type + // and standard packet size. We should not try setting up the BIOS + // in streaming mode as some versions of Windows will silently fail to do so. + + // However, our special wheelvkd will allow us to use streaming mode. + // wheelvkd signals it is running by using a the imex device IDs even + // before the knocking sequence: + if (data.device_id == PS2M_DEVICE_ID_STD) { + // Our special driver is NOT running... + dputs("Windows running, using standard packet size"); + data.bios_packet_size = PS2M_PACKET_SIZE_STD; } } #endif /* USE_WIN386 */ @@ -951,33 +1030,61 @@ static void reset_mouse_hardware() // Try to init PS/2 BIOS with desired packet size / streaming mode err = ps2m_init(data.bios_packet_size); - if (err && data.bios_packet_size != PS2M_PACKET_SIZE_PLAIN) { - // However, if there is an error, drop down to plain packet size + if (err && data.bios_packet_size != PS2M_PACKET_SIZE_STD) { + // However, if there is an error, drop down to std packet size // Emulators like DOSBox don't support anything but plain packet size - dputs("BIOS didn't support streaming mode, using plain packet size"); - data.bios_packet_size = PS2M_PACKET_SIZE_PLAIN; + dputs("BIOS doesn't support streaming mode, using standard packet size"); + data.bios_packet_size = PS2M_PACKET_SIZE_STD; err = ps2m_init(data.bios_packet_size); } if (err) { - dputs("error on ps2m_init during reset, ignoring"); + dputs("error on ps2m_init during reset"); + return false; } #if USE_WHEEL if (data.usewheel && data.bios_packet_size == PS2M_PACKET_SIZE_STREAMING - && ps2m_detect_wheel()) { - dputs("PS/2 wheel detected"); - data.haswheel = true; + && ps2m_detect_imps2()) { + dputs("ImPS/2 detected"); data.packet_size = PS2M_PACKET_SIZE_EXT; + + data.num_wheels = MIN(1, MAX_WHEELS); + ps2m_get_device_id(&data.device_id); + +#if USE_IMEX + // Try to go a bit further + if (data.device_id == PS2M_DEVICE_ID_IMPS2) { + ps2m_send_imex_sequence(); + + ps2m_get_device_id(&data.device_id); + if (data.device_id == PS2M_DEVICE_ID_IMEX) { + ps2m_send_imex_horz_sequence(); + + ps2m_get_device_id(&data.device_id); + // According to VirtualBox device ID will not change after this + // sequence. We don't need to check for it anyway as our code to + // handle both protocols is the same. + } + } + + if (data.device_id == PS2M_DEVICE_ID_IMEX + || data.device_id == PS2M_DEVICE_ID_IMEX_HORZ) { + data.num_wheels = MIN(2, MAX_WHEELS); + data.num_buttons = MIN(5, MAX_BUTTONS); + } +#endif /* USE_IMEX */ + + dprintf("found mouse device id = 0x%hx num buttons=%d num wheels=%d\n", + data.device_id, data.num_buttons, data.num_wheels); } else { if (data.usewheel) dputs("PS/2 wheel NOT detected"); - data.haswheel = false; } #if USE_VMWARE // With the VMware backdoor, we can get the wheel information even if // we couldn't configure the PS/2 mouse at all. if (data.vmwavail && data.usewheel) { - data.haswheel = true; + data.num_wheels = 1; } #endif /* USE_VMWARE */ #endif /* USE_WHEEL */ @@ -996,6 +1103,56 @@ static void reset_mouse_hardware() #endif ps2m_enable(true); + return true; +} + +#if USE_SERIAL +static bool reset_serial_mouse() +{ + // TODO: Currently this is not power-cycling nor redetecting the mouse + unsigned iobase = data.port_io; + + data.cur_packet_bytes = 0; + data.cur_packet_ticks = 0; + + while (serial_data_ready(iobase)) { + uint8_t byte = serial_read_data(iobase); + dprintf("got byte from serial %hx '%c'\n", byte, byte); + } + + // First, reconfigure the serial port to our liking + serial_configure_line(iobase, SERIAL_DIVISOR_1200, SERIAL_LCR_WL_7|SERIAL_LCR_PAR_NONE); + // Keep the mouse powered on + serial_set_modem_control(iobase, SERIAL_MCR_DTR | SERIAL_MCR_RTS); + // Reset and enable the FIFO + serial_set_fifo_control(iobase, SERIAL_FCR_FIFO_ENABLE|SERIAL_FCR_FIFO_RESET); + // And finally enable interrupts + serial_configure_interrupts(iobase, SERIAL_IER_ERBFI); + pic_unmask_irq(data.port_irq); + + dputs("Serial mouse reset done"); + + return true; +} +#endif + +static bool reset_mouse_hardware() +{ + dputs("Reset mouse hardware"); + + // I don't want to search for mouse again. + // So this won't switch from serial to PS/2. + // For that, reload the mouse driver. + + if (data.port == 0) { + return reset_ps2_mouse(); + } else { +#if USE_SERIAL + return reset_serial_mouse(); +#else + return false; +#endif + } } /** Reset "software" mouse settings, i.e. those configurable by the client program. */ @@ -1019,10 +1176,6 @@ static void reset_mouse_settings() data.cursor_hotspot.y = 0; memcpy(data.cursor_graphic, default_cursor_graphic, sizeof(data.cursor_graphic)); -#if USE_WHEEL - data.usewheelapi = false; -#endif - refresh_cursor(); // This will hide the cursor and update data.cursor_visible } @@ -1030,8 +1183,8 @@ static void reset_mouse_settings() static void reset_mouse_state() { int i; - data.pos.x = data.min.x; - data.pos.y = data.min.y; + data.pos.x = data.min.x + (data.max.x - data.min.x) / 2; + data.pos.y = data.min.y + (data.max.y - data.min.y) / 2; data.pos_frac.x = 0; data.pos_frac.y = 0; data.delta.x = 0; @@ -1041,7 +1194,7 @@ static void reset_mouse_state() data.abs_pos.x = -1; data.abs_pos.y = -1; data.buttons = 0; - for (i = 0; i < NUM_BUTTONS; i++) { + for (i = 0; i < MAX_BUTTONS; i++) { data.button[i].pressed.count = 0; data.button[i].pressed.last.x = 0; data.button[i].pressed.last.y = 0; @@ -1049,7 +1202,13 @@ static void reset_mouse_state() data.button[i].released.last.x = 0; data.button[i].released.last.y = 0; } - data.wheel_delta = 0; +#if USE_WHEEL + for (i = 0; i < MAX_WHEELS; i++) { + data.wheel[i].delta = 0; + data.wheel[i].last.x = 0; + data.wheel[i].last.y = 0; + } +#endif data.cursor_visible = false; data.cursor_pos.x = 0; data.cursor_pos.y = 0; @@ -1057,14 +1216,16 @@ static void reset_mouse_state() memset(data.cursor_prev_graphic, 0, sizeof(data.cursor_prev_graphic)); } +#if USE_WHEEL /** Return (in the appropiate registers) the wheel movement counter and afterwards reset it. */ -static void return_clear_wheel_counter(union INTPACK __far *r) +static void return_clear_wheel_counter(union INTPACK __far *r, struct wheelcounter *c) { - r->w.cx = snap_to_grid(data.wheel_last.x, data.screen_granularity.x); - r->w.dx = snap_to_grid(data.wheel_last.y, data.screen_granularity.y); - r->w.bx = data.wheel_delta; - data.wheel_delta = 0; + r->w.cx = snap_to_grid(c->last.x, data.screen_granularity.x); + r->w.dx = snap_to_grid(c->last.y, data.screen_granularity.y); + r->w.bx = c->delta; + c->delta = 0; } +#endif /** Return (in the appropiate registers) the desired button press counter and afterwards reset it. */ static void return_clear_button_counter(union INTPACK __far *r, struct buttoncounter *c) @@ -1098,10 +1259,14 @@ static void int33_handler(union INTPACK r) dputs("Mouse reset"); reload_video_info(); reset_mouse_settings(); - reset_mouse_hardware(); reset_mouse_state(); - r.w.ax = INT33_MOUSE_FOUND; - r.w.bx = NUM_BUTTONS; + if (reset_mouse_hardware()) { + r.w.ax = INT33_MOUSE_FOUND; + r.w.bx = data.num_buttons; + } else { + r.w.ax = 0; + r.w.bx = 0; + } break; case INT33_SHOW_CURSOR: if (data.hidden_count > 0) data.hidden_count--; @@ -1125,9 +1290,13 @@ static void int33_handler(union INTPACK r) r.w.dx = snap_to_grid(data.pos.y, data.screen_granularity.y); r.w.bx = data.buttons; #if USE_WHEEL - if (data.haswheel) { - r.h.bh = data.wheel_delta; - data.wheel_delta = 0; + if (data.num_wheels > 0) { + r.h.bh = data.wheel[0].delta; + data.wheel[0].delta = 0; + if (data.num_wheels > 1) { + r.h.ah = data.wheel[1].delta; + data.wheel[1].delta = 0; + } } #endif break; @@ -1151,18 +1320,19 @@ static void int33_handler(union INTPACK r) #endif r.w.ax = data.buttons; #if USE_WHEEL - if (data.haswheel) { - r.h.bh = data.wheel_delta; - if (r.w.bx == -1) { + if (data.num_wheels > 0) { + int n = -(int16_t)(r.w.bx); + r.h.ah = data.wheel[0].delta; + if (n >= 0 && n < MAX_WHEELS) { // Asked for wheel information - return_clear_wheel_counter(&r); + return_clear_wheel_counter(&r, &data.wheel[n]); break; } } #endif // Regular button information return_clear_button_counter(&r, - &data.button[MIN(r.w.bx, NUM_BUTTONS - 1)].pressed); + &data.button[MIN(r.w.bx, MAX_BUTTONS - 1)].pressed); break; case INT33_GET_BUTTON_RELEASED_COUNTER: #if TRACE_CALLS @@ -1170,17 +1340,18 @@ static void int33_handler(union INTPACK r) #endif r.w.ax = data.buttons; #if USE_WHEEL - if (data.haswheel) { - r.h.bh = data.wheel_delta; - if (r.w.bx == -1) { + if (data.num_wheels > 0) { + int n = -(int16_t)(r.w.bx); + r.h.ah = data.wheel[0].delta; + if (n >= 0 && n < MAX_WHEELS) { // Asked for wheel information - return_clear_wheel_counter(&r); + return_clear_wheel_counter(&r, &data.wheel[n]); break; } } #endif return_clear_button_counter(&r, - &data.button[MIN(r.w.bx, NUM_BUTTONS - 1)].released); + &data.button[MIN(r.w.bx, MAX_BUTTONS - 1)].released); break; case INT33_SET_HORIZONTAL_WINDOW: dprintf("Mouse set horizontal window [%d,%d]\n", r.w.cx, r.w.dx); @@ -1224,7 +1395,7 @@ static void int33_handler(union INTPACK r) data.delta.y = 0; break; case INT33_SET_EVENT_HANDLER: - dputs("Mouse set event handler"); + dprintf("Mouse set event handler mask=0x%x\n", r.w.cx); data.event_mask = r.w.cx; data.event_handler = MK_FP(r.w.es, r.w.dx); break; @@ -1281,13 +1452,16 @@ static void int33_handler(union INTPACK r) dputs("Mouse reset settings"); reload_video_info(); reset_mouse_settings(); + reset_mouse_state(); if (!data.bios_packet_size || !data.packet_size) { // Someone is calling this without calling reset first - reset_mouse_hardware(); + if (!reset_mouse_hardware()) { + r.w.ax = 0; + break; + } } - reset_mouse_state(); r.w.ax = INT33_MOUSE_FOUND; - r.w.bx = NUM_BUTTONS; + r.w.bx = data.num_buttons; break; case INT33_GET_LANGUAGE: r.w.bx = 0; @@ -1296,8 +1470,10 @@ static void int33_handler(union INTPACK r) dputs("Mouse get driver info"); r.h.bh = REPORTED_VERSION_MAJOR; r.h.bl = REPORTED_VERSION_MINOR; - r.h.ch = INT33_MOUSE_TYPE_PS2; - r.h.cl = 0; + // TODO Seems that windows may use this, albeit only when the following has MSB set? + r.h.ch = data.port == 0 ? INT33_MOUSE_TYPE_PS2 : INT33_MOUSE_TYPE_SERIAL; + r.h.cl = data.port_irq; + r.h.al = data.port; break; case INT33_GET_MAX_COORDINATES: r.w.bx = 0; @@ -1310,14 +1486,26 @@ static void int33_handler(union INTPACK r) r.w.cx = data.max.x; r.w.dx = data.max.y; break; + case INT33_GET_VERSION_STRING: + dputs("Mouse get version string"); + r.x.es = FP_SEG(&version_string); + r.x.di = FP_OFF(&version_string); + break; #if USE_WHEEL // Wheel API extensions: case INT33_GET_CAPABILITIES: dputs("Mouse get capabitilies"); r.w.ax = INT33_WHEEL_API_MAGIC; // Driver supports wheel API r.w.bx = 0; - r.w.cx = data.haswheel ? INT33_CAPABILITY_MOUSE_API : 0; - data.usewheelapi = true; // Someone calling this function likely wants to use wheel API + r.w.cx = 0; + if (data.num_wheels > 0) { + r.w.cx |= INT33_CAPABILITY_WHEEL_API; + if (data.num_wheels > 1) { + r.w.cx |= INT33_CAPABILITY_WHEEL2_API; + } + } + dprintf(" Returning capabilities=0x%x\n", r.w.cx); + data.usewheelapi = true; // Someone calling this function wants to use wheel API break; #endif // Our internal API extensions: @@ -1357,6 +1545,169 @@ void __declspec(naked) __far int33_isr(void) } } +#if USE_SERIAL +static void handle_serial_m_packet() +{ + int x = (int8_t) ((data.cur_packet[1] & SERMOUSE_MS_X_LO_MASK) + | ((data.cur_packet[0] & SERMOUSE_MS_X_HI_MASK) << SERMOUSE_MS_X_HI_SHIFT)); + int y = (int8_t) ((data.cur_packet[2] & SERMOUSE_MS_Y_LO_MASK) + | ((data.cur_packet[0] & SERMOUSE_MS_Y_HI_MASK) << SERMOUSE_MS_Y_HI_SHIFT)); + unsigned buttons = 0; + bool abs = false; + char wheeln = 0; + int z = 0; + + if (data.cur_packet[0] & SERMOUSE_MS_BUTTON_LEFT) + buttons |= INT33_BUTTON_MASK_LEFT; + if (data.cur_packet[0] & SERMOUSE_MS_BUTTON_RIGHT) + buttons |= INT33_BUTTON_MASK_RIGHT; + if (data.num_buttons >= 3 && (data.buttons & INT33_BUTTON_MASK_CENTER)) + buttons |= INT33_BUTTON_MASK_CENTER; // preserve status of mid button for now + +#if TRACE_PROTO + dprintf("serial decoded M packet buttons=%x x=%d y=%d\n", buttons, x, y); +#endif /* TRACE_PROTO */ + + handle_mouse_event(buttons, abs, x, y, wheeln, z); +} + +static void handle_serial_m3_packet() +{ + // No need to send mouse movement as it will have been processed + // back when we received the 3rd byte. + // Instead focus on wheel and mid mouse button which come in the 4rd byte. + int x = 0; + int y = 0; + // Preserve old button state for most part + unsigned buttons = data.buttons & (INT33_BUTTON_MASK_LEFT|INT33_BUTTON_MASK_RIGHT); + bool abs = false; + char wheeln = 0; // only one wheel supported for now + int z = sign_extend(data.cur_packet[3] & SERMOUSE_MS_Z_LO_MASK, 4); + + if (data.cur_packet[3] & SERMOUSE_MS_BUTTON_CENTER) { + buttons |= INT33_BUTTON_MASK_CENTER; + } + +#if TRACE_PROTO + dprintf("serial decoded M3 packet buttons=%x z=%d\n", buttons, z); +#endif /* TRACE_PROTO */ + + // Only send 2nd event if really necessary + if (buttons != data.buttons || z) { + handle_mouse_event(buttons, abs, x, y, wheeln, z); + } +} + +static void handle_serial_data(uint8_t byte) +{ + // TODO: not checking cur_packet_ticks for now, don't think it necessary for serial + // due to different synchronization + // Depending on the serial protocol now... + if (data.device_id == SERMOUSE_DEVICE_ID_MS) { + // All these protocols use bit #6 (MSB since we use 7 data bits) as Start-of-Frame indicator + if (byte & (1 << 6)) { + if (data.buttons & INT33_BUTTON_MASK_CENTER && data.cur_packet_bytes == 3) { + // Was the previous packet a 3byte one and the middle button is down? + // Assume the mouse just skipped the 4th byte and release the middle button + // TODO I am not sure if this hack is required +#if TRACE_PROTO + dputs("serial releasing mid button"); +#endif /* TRACE_PROTO */ + handle_mouse_event(data.buttons & ~INT33_BUTTON_MASK_CENTER, false, 0, 0, 0, 0); + } + + // Prepare for new packet + data.cur_packet_bytes = 0; + } + +#if TRACE_PROTO + dprintf("serial byte %d/%d = %hx\n", + 1 + data.cur_packet_bytes, data.packet_size, byte); +#endif /* TRACE_PROTO */ + + data.cur_packet[data.cur_packet_bytes] = byte; + data.cur_packet_bytes++; + + // However there is no End-of-Frame indicator. + // We do not know if this is going to be a 3 byte or 4 byte packet + // (apparently 4-byte packet mouses may send up a 3-byte packet + // if no wheel or extra buttons are modified) + // So we will send a "main" mouse event when we receive the 3rd byte + // and then follow up with a second mouse event if we receive the 4rd. + + if (data.cur_packet_bytes == 3) { + // send main mouse event now + handle_serial_m_packet(); + } else if (data.cur_packet_bytes == 4) { + // handle the 4th byte + handle_serial_m3_packet(); + } + + if (data.cur_packet_bytes >= data.packet_size) { + data.cur_packet_bytes = 0; + } + } +} + +static void irq3_4_handler() +#pragma aux irq3_4_handler "*" parm caller [] modify [ax bx cx dx es] +{ + uint16_t iobase = data.port_io; + uint8_t intid, byte; + + pic_eoi_irq(data.port_irq); + + while ((intid = serial_read_pending_interrupt(iobase)) != SERIAL_IID_NONE) { + switch (intid) { + case SERIAL_IID_ERROR: + byte = serial_get_line_status(iobase); + // If there is an error, we should likely clear our packet buffer + if (byte & SERIAL_LSR_OE | SERIAL_LSR_PE | SERIAL_LSR_FE) { + dputs("serial rx error"); + data.cur_packet_bytes = 0; + } + break; + case SERIAL_IID_DATA: + case SERIAL_IID_STALE: + while (serial_data_ready(iobase)) { + byte = serial_read_data(iobase); + handle_serial_data(byte); + } + break; + case SERIAL_IID_MODEM: + byte = serial_get_modem_status(iobase); // Read but ignore + break; + } + } +} + +void __declspec(naked) __far irq3_4_isr(void) +{ + __asm { + pusha + push ds + push es + push fs + push gs + + mov bp, sp + push cs + pop ds + + call irq3_4_handler + + pop gs + pop fs + pop es + pop ds + popa + + ; We don't chain to the previous handler + iret + } +} +#endif + #if USE_WIN386 /** Windows will call this function to notify events when we are inside a DOS box. */ static void windows_mouse_handler(int action, int x, int y, int buttons, int events) @@ -1366,7 +1717,7 @@ static void windows_mouse_handler(int action, int x, int y, int buttons, int eve case VMD_ACTION_MOUSE_EVENT: (void) events; // Forward event to our internal system - handle_mouse_event(buttons, true, x, y, 0); + handle_mouse_event(buttons, true, x, y, 0, 0); break; case VMD_ACTION_HIDE_CURSOR: dputs("VMD_ACTION_HIDE_CURSOR"); @@ -1472,7 +1823,7 @@ void __declspec(naked) __far int2f_isr(void) popa ; Jump to the next handler in the chain - jmp dword ptr cs:[data + 4] ; wasm doesn't support structs, this is data.prev_int2f_handler + jmp dword ptr cs:[data + (4*2)] ; wasm doesn't support structs, this is data.prev_int2f_handler } } #endif @@ -1490,7 +1841,13 @@ static LPTSRDATA int33_get_tsr_data(void); LPTSRDATA __far get_tsr_data(bool installed) { if (installed) { - return int33_get_tsr_data(); + // Check if int33 ISR actually points to something before calling it. + if (_dos_getvect(0x33)) { + // Call our magic function, which also returns our data segment + return int33_get_tsr_data(); + } else { + return 0; + } } else { // Get the TSR data of this instance, not the one currently installed // This is as simple as getting the data from this segment |
