aboutsummaryrefslogtreecommitdiff
path: root/mousetsr.c
diff options
context:
space:
mode:
authorJavier <dev.git@javispedro.com>2026-08-30 02:21:09 +0200
committerJavier <dev.git@javispedro.com>2026-08-30 02:21:09 +0200
commit1b0344660de2832b472b0a413c50ff7f111a9293 (patch)
tree5ae7303643d6c9187da6366cf30a4f6a4c73d54b /mousetsr.c
parent003a26a76982f512e2f3a1108e46b88aa29ab306 (diff)
downloadvbados-1b0344660de2832b472b0a413c50ff7f111a9293.tar.gz
vbados-1b0344660de2832b472b0a413c50ff7f111a9293.zip
first steps towards (optional) serial mouse support
Diffstat (limited to 'mousetsr.c')
-rw-r--r--mousetsr.c246
1 files changed, 215 insertions, 31 deletions
diff --git a/mousetsr.c b/mousetsr.c
index 2c387e4..b386c89 100644
--- a/mousetsr.c
+++ b/mousetsr.c
@@ -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