diff options
| author | Javier <dev.git@javispedro.com> | 2026-08-30 02:21:09 +0200 |
|---|---|---|
| committer | Javier <dev.git@javispedro.com> | 2026-08-30 02:21:09 +0200 |
| commit | 1b0344660de2832b472b0a413c50ff7f111a9293 (patch) | |
| tree | 5ae7303643d6c9187da6366cf30a4f6a4c73d54b /mousetsr.c | |
| parent | 003a26a76982f512e2f3a1108e46b88aa29ab306 (diff) | |
| download | vbados-1b0344660de2832b472b0a413c50ff7f111a9293.tar.gz vbados-1b0344660de2832b472b0a413c50ff7f111a9293.zip | |
first steps towards (optional) serial mouse support
Diffstat (limited to 'mousetsr.c')
| -rw-r--r-- | mousetsr.c | 246 |
1 files changed, 215 insertions, 31 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" @@ -737,11 +740,11 @@ static void handle_mouse_event(uint16_t buttons, bool absolute, int x, int y, ch static void handle_ps2_packet(void) { - unsigned status = data.ps2_packet[0]; + unsigned status = data.cur_packet[0]; // Decode basic PS/2 packet unsigned buttons = status & (PS2M_STATUS_BUTTON_1 | PS2M_STATUS_BUTTON_2 | PS2M_STATUS_BUTTON_3); - int x = data.ps2_packet[1], y = data.ps2_packet[2]; + int x = data.cur_packet[1], y = data.cur_packet[2]; // For the extended byte... bool abs = false; @@ -757,32 +760,32 @@ static void handle_ps2_packet(void) switch (data.device_id) { case PS2M_DEVICE_ID_IMPS2: // ImPS/2. The fourth packet is the (vertical) wheel movement. - z = (int8_t) data.ps2_packet[3]; + 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.ps2_packet[3] & PS2M_IMEX_VERTICAL_SCROLL) { + if (data.cur_packet[3] & PS2M_IMEX_VERTICAL_SCROLL) { // Vertical scrolling, with 6 bits of precision. - z = sign_extend(data.ps2_packet[3], 6); + 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.ps2_packet[3] & PS2M_IMEX_HORIZONTAL_SCROLL) { + } else if (data.cur_packet[3] & PS2M_IMEX_HORIZONTAL_SCROLL) { // Horizontal scrolling, with 6 bits of precision. - z = sign_extend(data.ps2_packet[3], 6); + 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.ps2_packet[3] & PS2M_IMEX_BUTTON_4) { + if (data.cur_packet[3] & PS2M_IMEX_BUTTON_4) { buttons |= INT33_BUTTON_MASK_4TH; } - if (data.ps2_packet[3] & PS2M_IMEX_BUTTON_5) { + 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.ps2_packet[3], 4); + z = sign_extend(data.cur_packet[3], 4); } break; #endif /* USE_IMEX */ @@ -848,13 +851,13 @@ static void handle_ps2_packet(void) } if (vmw.status & VMWARE_ABSPOINTER_STATUS_BUTTON_LEFT) { - buttons |= PS2M_STATUS_BUTTON_1; + buttons |= INT33_BUTTON_MASK_LEFT; } if (vmw.status & VMWARE_ABSPOINTER_STATUS_BUTTON_RIGHT) { - buttons |= PS2M_STATUS_BUTTON_2; + buttons |= INT33_BUTTON_MASK_RIGHT; } if (vmw.status & VMWARE_ABSPOINTER_STATUS_BUTTON_MIDDLE) { - buttons |= PS2M_STATUS_BUTTON_3; + buttons |= INT33_BUTTON_MASK_CENTER; } } else { return; // Ignore the PS/2 packet otherwise, it is likely garbage @@ -876,9 +879,9 @@ static void ps2_mouse_handler(uint16_t word0, uint16_t word1, uint16_t word2, ui // Are we using the BIOS in 3-packet mode directly? 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; @@ -889,7 +892,7 @@ static void ps2_mouse_handler(uint16_t word0, uint16_t word1, uint16_t word2, ui // We have to compute synchronization ourselves. 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! cur_bytes=%u prev_ticks=%u new_ticks=%u\n", data.cur_packet_bytes, data.cur_packet_ticks, ticks); @@ -900,11 +903,11 @@ static void ps2_mouse_handler(uint16_t word0, uint16_t word1, uint16_t word2, ui } #if TRACE_PROTO - dprintf("ps2 callback byte %d/%d = %x\n", + dprintf("ps2 callback byte %d/%d = %hx\n", 1 + data.cur_packet_bytes, data.packet_size, word0 & 0xFF); #endif /* TRACE_PROTO */ - data.ps2_packet[data.cur_packet_bytes] = word0; + data.cur_packet[data.cur_packet_bytes] = word0; data.cur_packet_bytes++; if (data.cur_packet_bytes >= data.packet_size) { @@ -985,13 +988,14 @@ 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_STD; data.cur_packet_bytes = 0; @@ -1034,7 +1038,8 @@ static void reset_mouse_hardware() 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 @@ -1065,7 +1070,6 @@ static void reset_mouse_hardware() if (data.device_id == PS2M_DEVICE_ID_IMEX || data.device_id == PS2M_DEVICE_ID_IMEX_HORZ) { - dputs("ImEx detected"); data.num_wheels = MIN(2, MAX_WHEELS); data.num_buttons = MIN(5, MAX_BUTTONS); } @@ -1099,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. */ @@ -1205,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 = data.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--; @@ -1394,11 +1452,14 @@ 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 = data.num_buttons; break; @@ -1409,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; @@ -1482,6 +1545,121 @@ void __declspec(naked) __far int33_isr(void) } } +#if USE_SERIAL +static void handle_serial_ms_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; + // Unused: + 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 TRACE_PROTO + dprintf("serial decoded 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_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... + switch (data.device_id) { + case 'M': + case '3': + // All these protocols use bit #6 (MSB since we use 7 data bits) as Start-of-Frame indicator + if (byte & (1 << 6)) { + 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++; + + if (data.cur_packet_bytes >= data.packet_size) { + handle_serial_ms_packet(); + data.cur_packet_bytes = 0; + } + + break; + } +} + +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) @@ -1597,7 +1775,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 @@ -1615,7 +1793,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 |
