aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bda.h21
-rw-r--r--dlog.h41
-rw-r--r--int15ps2.h7
-rw-r--r--mousetsr.c246
-rw-r--r--mousetsr.h39
-rw-r--r--mousmain.c306
-rw-r--r--nls/vbmouse.en8
-rw-r--r--pic.h59
-rw-r--r--serial.h117
-rw-r--r--sermouse.h38
-rw-r--r--version.h2
11 files changed, 782 insertions, 102 deletions
diff --git a/bda.h b/bda.h
index 29b842a..c479182 100644
--- a/bda.h
+++ b/bda.h
@@ -1,3 +1,22 @@
+/*
+ * VBMouse - BIOS data area access routines
+ * Copyright (C) 2022 Javier S. Pedro
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
#ifndef BDA_H
#define BDA_H
@@ -22,6 +41,8 @@ static inline uint8_t bda_get_byte(unsigned int offset) {
#define bda_get_ebda_segment() bda_get_word(0x0e)
+#define bda_get_equipment() bda_get_word(0x10)
+
#define bda_get_video_mode() bda_get_byte(0x49)
#define bda_get_num_columns() bda_get_word(0x4a)
#define bda_get_video_page_size() bda_get_word(0x4c)
diff --git a/dlog.h b/dlog.h
index 112254e..2878ac9 100644
--- a/dlog.h
+++ b/dlog.h
@@ -29,14 +29,18 @@
/** If 0, these routines become nops */
#define ENABLE_DLOG 0
/** 1 means target serial port, 0 means target IO port. */
-#define DLOG_TARGET_SERIAL 0
-/** IO port to target.
+#define DLOG_TARGET DLOG_TARGET_IOPORT
+/** When using DLOG_TARGET_IOPORT, which IO port to target.
* VirtualBox uses 0x504, Bochs, DOSBox and descendants use 0xE9.
- * When using DLOG_TARGET_SERIAL, use desired UART IO base port. (e.g. COM1 = 0x3F8). */
+ * When using DLOG_TARGET_SERIAL, the desired UART IO base port. (e.g. COM1 = 0x3F8). */
#define DLOG_TARGET_PORT 0x504
// End of customizable defines
+#define DLOG_TARGET_BIOSTTY 1
+#define DLOG_TARGET_SERIAL 2
+#define DLOG_TARGET_IOPORT 3
+
#if ENABLE_DLOG
/** Initializes the debug log port. */
@@ -45,7 +49,22 @@ static void dlog_init();
/** Logs a single character to the debug message IO port. */
static inline void dputc(char c);
-#if DLOG_TARGET_SERIAL
+#if DLOG_TARGET == DLOG_TARGET_BIOSTTY
+
+static inline void dlog_init()
+{
+ // No initialization required
+}
+
+static inline void dputc(char c);
+#pragma aux dputc = \
+ "mov ah, 0x0E" \
+ "xor bx, bx" \
+ "int 0x10" \
+ __parm [AL] \
+ __modify __exact [AH AL BH BL];
+
+#elif DLOG_TARGET == DLOG_TARGET_SERIAL
static void dlog_init()
{
@@ -65,7 +84,7 @@ static inline void dputc(char c)
outp(DLOG_TARGET_PORT, c);
}
-#else /* DLOG_TARGET_SERIAL */
+#elif DLOG_TARGET == DLOG_TARGET_IOPORT
static inline void dlog_init()
{
@@ -77,7 +96,7 @@ static inline void dputc(char c)
outp(DLOG_TARGET_PORT, c);
}
-#endif /* DLOG_TARGET_SERIAL */
+#endif /* DLOG_TARGET */
static void d_utoa(unsigned num, unsigned base)
{
@@ -265,6 +284,16 @@ static void dprintf(const char *fmt, ...)
break;
}
break;
+ case 'b':
+ switch (size) {
+ case sizeof(int):
+ d_utoa(va_arg(va, int), 2);
+ break;
+ case sizeof(long):
+ d_ultoa(va_arg(va, long), 2);
+ break;
+ }
+ break;
case 'p':
if (is_far) {
void __far *p = va_arg(va, void __far *);
diff --git a/int15ps2.h b/int15ps2.h
index db1b878..04d5a8d 100644
--- a/int15ps2.h
+++ b/int15ps2.h
@@ -23,6 +23,7 @@
#include <stdbool.h>
#include <stdint.h>
#include "utils.h"
+#include "bda.h"
/** Standard PS/2 mouse IRQ. At least on VirtualBox. */
#define PS2_MOUSE_IRQ 12
@@ -95,6 +96,12 @@ enum ps2m_sample_rate {
/** Invoked by the BIOS when there is a mouse event. */
typedef void (__far * LPFN_PS2CALLBACK)();
+static bool ps2m_installed()
+{
+ uint16_t equipment = bda_get_equipment();
+ return equipment & (1 << 2);
+}
+
static ps2m_err ps2m_init(uint8_t packet_size);
#pragma aux ps2m_init = \
"stc" /* If nothing happens, assume failure */ \
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
diff --git a/mousetsr.h b/mousetsr.h
index f33dc11..704f670 100644
--- a/mousetsr.h
+++ b/mousetsr.h
@@ -38,6 +38,8 @@
#define USE_WHEEL 1
/** Enable Intellimouse Explorer protocol (two wheels and 5 buttons). */
#define USE_IMEX 1
+/** Enable support for serial mouse. */
+#define USE_SERIAL 1
/** Trace mouse events verbosily. */
#define TRACE_EVENTS 0
/** Trace (noisy) API calls. */
@@ -53,11 +55,14 @@
#define USE_INTEGRATION (USE_VIRTUALBOX || USE_VMWARE)
-/** Max size of PS/2 packet that we support. */
-#define MAX_PS2_PACKET_SIZE 4
+/** Max size of PS/2/serial packet that we support. */
+#define MAX_PACKET_SIZE 4
-/** Maximum number of 55ms ticks that may pass between two bytes of the same PS/2 packet */
-#define MAX_PS2_PACKET_DELAY 2
+/** Maximum number of 55ms ticks that may pass between two bytes of the same packet */
+#define MAX_PACKET_DELAY 2
+
+/** Maximum COMn port. */
+#define MAX_PORTS 4
/** Maximum number of buttons supported by this driver. */
#define MAX_BUTTONS 5
@@ -97,11 +102,15 @@ enum {
typedef struct tsrdata {
// TSR installation data
+ // Note that because of WatcomC limitations the offsets of these
+ // are hardcoded in the inline ASM, so do not change them.
/** Previous int33 ISR, storing it for uninstall. */
void (__interrupt __far *prev_int33_handler)();
-#if USE_WIN386
+ /** Previous ISR for IRQ 3 or 4, depending on the used serial port. */
+ void (__interrupt __far *prev_irq3_4_handler)();
+ /** Previous int2f ISR, may need to chain to it. */
void (__interrupt __far *prev_int2f_handler)();
-#endif
+
// Settings configured via the command line
#if USE_WHEEL
/** Whether to enable & use wheel mouse. */
@@ -126,24 +135,28 @@ typedef struct tsrdata {
struct point screen_granularity;
// Detected mouse hardware & status
- /** Current negotiated PS/2 device_id */
+ /** Current mouse's port. 0 = PS/2 , 1...N serial. */
+ uint8_t port;
+ /** Current negotiated PS/2 device_id (or serial mouse type) */
uint8_t device_id;
/** Number of buttons of current device. */
uint8_t num_buttons;
-#if USE_WHEEL
/** Number of wheels of current device. */
uint8_t num_wheels;
-#endif
- /** Packet size that the BIOS is currently using. Either 1 (streaming) or 3 (plain). */
+ /** Packet size that the PS/2 BIOS is currently using. Either 1 (streaming) or 3 (plain). */
uint8_t bios_packet_size;
/** Packet size that we are currently expecting internally. Usually 3 (plain) or 4 (with wheel). */
uint8_t packet_size;
/** For streaming mode: number of bytes received so far (< packet_size). */
uint8_t cur_packet_bytes;
- /** Stores the bytes received so far (cur_bytes). */
- uint8_t ps2_packet[MAX_PS2_PACKET_SIZE];
+ /** Stores the bytes received so far (cur_packet_bytes). */
+ uint8_t cur_packet[MAX_PACKET_SIZE];
/** Number of ticks at the point when we started to receive this packet. */
uint16_t cur_packet_ticks;
+ /** IO base for the current port. */
+ uint16_t port_io;
+ /** IRQ for the current port. */
+ uint8_t port_irq;
// Current mouse settings
/** Mouse sensitivity/speed. */
@@ -255,6 +268,8 @@ typedef TSRDATA __far * LPTSRDATA;
extern void __declspec(naked) __far int33_isr(void);
+extern void __declspec(naked) __far irq3_4_isr(void);
+
extern void __declspec(naked) __far int2f_isr(void);
extern LPTSRDATA __far get_tsr_data(bool installed);
diff --git a/mousmain.c b/mousmain.c
index 07ba05a..a7298a5 100644
--- a/mousmain.c
+++ b/mousmain.c
@@ -28,6 +28,7 @@
#include "int33.h"
#include "int21dos.h"
#include "int15ps2.h"
+#include "serial.h"
#include "vbox.h"
#include "vmware.h"
#include "dostsr.h"
@@ -43,13 +44,15 @@ static bool detect_wheel(LPTSRDATA data)
if (ps2m_detect_imps2()) {
if (ps2m_detect_imex()) {
data->num_wheels = 2;
+ data->num_buttons = 5;
} else {
data->num_wheels = 1;
+ data->num_buttons = 3;
}
- printf(_(1, 0, "Wheel mouse found and enabled\n"));
return true;
} else {
data->num_wheels = 0;
+ data->num_buttons = 3;
return false;
}
}
@@ -124,7 +127,7 @@ static int set_hwheel_key(LPTSRDATA data, const char *keyname)
#endif /* USE_WHEEL */
#if USE_VIRTUALBOX
-static int set_virtualbox_integration(LPTSRDATA data, bool enable)
+static int set_virtualbox_integration(LPTSRDATA data, bool enable, bool verbose)
{
if (enable) {
int err;
@@ -133,19 +136,19 @@ static int set_virtualbox_integration(LPTSRDATA data, bool enable)
err = vbox_init_device(&data->vb);
if (err) {
- fprintf(stderr, _(3, 3, "Cannot find VirtualBox PCI device, err=%d\n"), err);
+ if (verbose) fprintf(stderr, _(3, 3, "Cannot find VirtualBox PCI device, err=%d\n"), err);
return err;
}
err = vbox_init_buffer(&data->vb, VBOX_BUFFER_SIZE);
if (err) {
- fprintf(stderr, _(3, 4, "Cannot lock buffer used for VirtualBox communication, err=%d\n"), err);
+ if (verbose) fprintf(stderr, _(3, 4, "Cannot lock buffer used for VirtualBox communication, err=%d\n"), err);
return err;
}
err = vbox_report_guest_info(&data->vb, VBOXOSTYPE_DOS);
if (err) {
- fprintf(stderr, _(3, 5, "VirtualBox communication is not working, err=%d\n"), err);
+ if (verbose) fprintf(stderr, _(3, 5, "VirtualBox communication is not working, err=%d\n"), err);
return err;
}
@@ -162,7 +165,7 @@ static int set_virtualbox_integration(LPTSRDATA data, bool enable)
data->vbavail = false;
data->vbhaveabs = false;
} else {
- printf(_(1, 9, "VirtualBox integration already disabled or not available\n"));
+ if (verbose) printf(_(1, 9, "VirtualBox integration already disabled or not available\n"));
}
}
@@ -179,7 +182,7 @@ static int set_virtualbox_host_cursor(LPTSRDATA data, bool enable)
#endif
#if USE_VMWARE
-static int set_vmware_integration(LPTSRDATA data, bool enable)
+static int set_vmware_integration(LPTSRDATA data, bool enable, bool verbose)
{
if (enable) {
int32_t version;
@@ -190,11 +193,11 @@ static int set_vmware_integration(LPTSRDATA data, bool enable)
version = vmware_get_version();
if (version < 0) {
- fprintf(stderr, _(3, 6, "Could not detect VMware, err=%ld\n"), version);
+ if (verbose) fprintf(stderr, _(3, 6, "Could not detect VMware, err=%ld\n"), version);
return -1;
}
- printf(_(1, 11, "Found VMware protocol version %ld\n"), version);
+ if (verbose) printf(_(1, 11, "Found VMware protocol version %ld\n"), version);
vmware_abspointer_cmd(VMWARE_ABSPOINTER_CMD_ENABLE);
@@ -220,7 +223,7 @@ static int set_vmware_integration(LPTSRDATA data, bool enable)
data->vmwavail = false;
printf(_(1, 13, "Disabled VMware integration\n"));
} else {
- printf(_(1, 14, "VMware integration already disabled or not available\n"));
+ if (verbose) printf(_(1, 14, "VMware integration already disabled or not available\n"));
}
}
@@ -228,7 +231,7 @@ static int set_vmware_integration(LPTSRDATA data, bool enable)
}
#endif
-static int set_integration(LPTSRDATA data, bool enable)
+static int set_integration(LPTSRDATA data, bool enable, bool verbose)
{
if (enable) {
int err = -1;
@@ -236,13 +239,13 @@ static int set_integration(LPTSRDATA data, bool enable)
#if USE_VIRTUALBOX
// First check if we can enable the VirtualBox integration,
// since it's a PCI device it's easier to check if it's not present
- err = set_virtualbox_integration(data, true);
+ err = set_virtualbox_integration(data, true, verbose);
if (!err) return 0;
#endif
#if USE_VMWARE
// Afterwards try VMWare integration
- err = set_vmware_integration(data, true);
+ err = set_vmware_integration(data, true, verbose);
if (!err) return 0;
#endif
@@ -251,12 +254,12 @@ static int set_integration(LPTSRDATA data, bool enable)
} else {
#if USE_VIRTUALBOX
if (data->vbavail) {
- set_virtualbox_integration(data, false);
+ set_virtualbox_integration(data, false, verbose);
}
#endif
#if USE_VMWARE
if (data->vmwavail) {
- set_vmware_integration(data, false);
+ set_vmware_integration(data, false, verbose);
}
#endif
return 0;
@@ -274,29 +277,33 @@ static int set_host_cursor(LPTSRDATA data, bool enable)
return -1;
}
-static int configure_driver(LPTSRDATA data)
+static int configure_driver_ps2(LPTSRDATA data)
{
int err;
- // Configure the debug logging port
- dlog_init();
-
// Check for PS/2 mouse BIOS availability
if ((err = ps2m_init(PS2M_PACKET_SIZE_STD))) {
- fprintf(stderr, _(3, 8, "Cannot init PS/2 mouse BIOS, err=%d\n"), err);
- // Can't do anything without PS/2
return err;
}
+ // OK, if BIOS gives no error, assume PS/2 mouse is present and use it
+ data->port = 0;
+ data->port_io = 0;
+ data->port_irq = 0;
+
#if USE_WHEEL
// Let's utilize the wheel by default
data->usewheel = true;
- detect_wheel(data); // Prints message if wheel found
+ detect_wheel(data);
+#else
+ // Without ImEX/wheel detection, hardcode number of buttons
+ data->num_buttons = 3;
+ data->num_wheels = 0;
#endif
#if USE_INTEGRATION
// Enable integration by default
- set_integration(data, true);
+ set_integration(data, true, false);
#endif
#if USE_VIRTUALBOX
@@ -307,6 +314,168 @@ static int configure_driver(LPTSRDATA data)
return 0;
}
+#if USE_SERIAL
+static int configure_driver_serial(LPTSRDATA data, int port, unsigned iobase)
+{
+ struct serial_config serconfig;
+ unsigned delay;
+ char sig1 = 0, sig2 = 0;
+
+ if (!iobase) {
+ dprintf("Skip search on COM%u because no IO base found\n", port);
+ return -1;
+ }
+
+#if ENABLE_DLOG && DLOG_TARGET == DLOG_TARGET_SERIAL
+ if (iobase == DLOG_TARGET_PORT) {
+ dprintf("Skip search on COM%u because it would overlap with dlog %x\n", port, iobase);
+ }
+#endif
+
+ dprintf("Detecting mouse on COM%d, iobase %xh\n", port, iobase);
+ serial_save_config(iobase, &serconfig);
+ serial_configure_interrupts(iobase, SERIAL_IER_NONE); // disable interrupts for now
+ // 1200 bps, word length = 7, parity = none
+ serial_configure_line(iobase, SERIAL_DIVISOR_1200, SERIAL_LCR_WL_7|SERIAL_LCR_PAR_NONE);
+ serial_set_fifo_control(iobase, SERIAL_FCR_FIFO_RESET|SERIAL_FCR_FIFO_ENABLE);
+ // Enables DRT, but clears RTS, so that mouse enters reset state
+ serial_set_modem_control(iobase, SERIAL_MCR_DTR);
+ dputs("DTR ON, RTS OFF");
+
+ // Wait for 3 ticks (100-150ms) for mouse to power off
+ bda_wait_ticks(3);
+
+ // Now turn on the mouse by enabling RTS
+ serial_set_modem_control(iobase, SERIAL_MCR_DTR | SERIAL_MCR_RTS);
+ dputs("DTR ON, RTS ON");
+
+ // Wait up to 10 ticks (550ms) for the mouse to send something we understand
+ for (delay = 10; delay > 0; delay--) {
+ while (serial_data_ready(iobase)) {
+ uint8_t car = serial_read_data(iobase);
+
+ // Is this something we recognize ?
+ dprintf("received serial data after reset: 0x%hx '%c'\n", car, car);
+ if (car == 'M') {
+ // Ok, got the MS signature, break out of this wait
+ sig1 = car;
+ break;
+ }
+ }
+
+ if (sig1) break;
+ bda_wait_tick();
+ dprintf("remaining ticks %u\n", delay);
+ }
+
+
+ if (sig1 == 'M') {
+ // We have a valid MS-style serial mouse
+ data->port = port;
+ data->device_id = 'M';
+ data->port_io = iobase;
+ data->port_irq = serial_get_irq(port);
+ data->num_buttons = 2;
+#if USE_WHEEL
+ data->num_wheels = 0;
+#endif
+ data->packet_size = 3;
+
+ // Should we wait for an extended signature? Let's do it
+ for (delay = 2; delay > 0; delay--) {
+ while (serial_data_ready(iobase)) {
+ uint8_t car = serial_read_data(iobase);
+
+ dprintf("received serial data after '%c': 0x%hx '%c'\n", sig1, car, car);
+ if (car == '3') {
+ // Ok, got the MS signature, break out of this wait
+ sig2 = car;
+ break;
+ }
+ }
+
+ if (sig2) break;
+ bda_wait_tick();
+ dprintf("2nd loop remaining ticks %u\n", delay);
+ }
+
+ if (sig2 == '3') {
+ data->device_id = '3';
+ data->num_buttons = 3;
+ }
+
+ // In any case, we got ourselves a mouse.
+ return 0;
+ }
+
+ // No mouse found on this port.
+ serial_restore_config(iobase, &serconfig);
+ return -2;
+}
+#endif
+
+/// Auto-configure the TSR. Try to detect which mouse we have connected and enable all possible integrations.
+static int configure_driver(LPTSRDATA data, unsigned num_ports, int ports[])
+{
+ int errs[MAX_PORTS+1] = {0};
+ unsigned portidx;
+
+ // Configure the debug logging port
+ dlog_init();
+
+ for (portidx = 0; portidx < num_ports; portidx++) {
+ int port = ports[portidx];
+ if (port == 0) {
+ // PS/2
+ int err = configure_driver_ps2(data);
+ if (!err) {
+ unsigned num_wheels = 0;
+#if USE_WHEEL
+ num_wheels = data->num_wheels;
+#endif
+ printf(_(1, 0, "Found PS/2 mouse with %u buttons, %u wheels\n"), data->num_buttons, num_wheels);
+ return 0;
+ } else {
+ // Remember error to print out later only if no mouse was found
+ errs[portidx] = err;
+ }
+ } else {
+#if USE_SERIAL
+ int err = configure_driver_serial(data, port, serial_get_iobase(port));
+ if (!err) {
+ unsigned num_wheels = 0;
+#if USE_WHEEL
+ num_wheels = data->num_wheels;
+#endif
+ printf(_(1, 22, "Found serial mouse on COM%u with %u buttons, %u wheels\n"), port, data->num_buttons, num_wheels);
+ return 0;
+ } else {
+ errs[portidx] = err;
+ }
+#endif
+ }
+ }
+
+ // If we are here, we have neither serial nor PS/2.
+ // Print all error messages now.
+ for (portidx = 0; portidx < num_ports; portidx++) {
+ int port = ports[portidx];
+ int err = errs[portidx];
+
+ if (err) {
+ if (port == 0) {
+ fprintf(stderr, _(3, 8, "Cannot init PS/2 mouse BIOS, err=%d\n"), err);
+ } else {
+ fprintf(stderr, _(3, 15, "Cannot find mouse in COM%u, err=%d\n"), port, err);
+ }
+ }
+ }
+
+ fprintf(stderr, _(3, 14, "No mouse found\n"));
+
+ return -1;
+}
+
static int move_driver_to_umb(LPTSRDATA __far * data)
{
segment_t cur_seg = FP_SEG(*data);
@@ -332,6 +501,16 @@ static __declspec(aborts) int install_driver(LPTSRDATA data, bool high)
data->prev_int33_handler = _dos_getvect(0x33);
_dos_setvect(0x33, data:>int33_isr);
+ data->prev_irq3_4_handler = 0;
+#if USE_SERIAL
+ if (data->port_irq) {
+ dprintf("Hooking irq%hu (int%x) for serial\n", data->port_irq, 0x8 + data->port_irq);
+ data->prev_irq3_4_handler = _dos_getvect(0x8 + data->port_irq);
+ _dos_setvect(0x8 + data->port_irq, data:>irq3_4_isr);
+ }
+#endif
+
+ data->prev_int2f_handler = 0;
#if USE_WIN386
data->prev_int2f_handler = _dos_getvect(0x2f);
_dos_setvect(0x2f, data:>int2f_isr);
@@ -359,16 +538,27 @@ static bool check_if_driver_uninstallable(LPTSRDATA data)
// Compare the segment of the installed handler to see if its ours
// or someone else's
if (FP_SEG(cur_int33_handler) != FP_SEG(data)) {
- fprintf(stderr, _(3, 9, "INT33 has been hooked by someone else, cannot safely remove\n"));
+ fprintf(stderr, _(3, 9, "INT%X has been hooked by someone else, cannot safely remove\n"), 0x33);
return false;
}
+#if USE_SERIAL
+ if (data->port_irq) {
+ void (__interrupt __far *cur_irq3_4_handler)() = _dos_getvect(0x8 + data->port_irq);
+
+ if (FP_SEG(cur_irq3_4_handler) != FP_SEG(data)) {
+ fprintf(stderr, _(3, 9, "INT%X has been hooked by someone else, cannot safely remove\n"), 0x8 + data->port_irq);
+ return false;
+ }
+ }
+#endif
+
#if USE_WIN386
{
void (__interrupt __far *cur_int2f_handler)() = _dos_getvect(0x2f);
if (FP_SEG(cur_int2f_handler) != FP_SEG(data)) {
- fprintf(stderr, _(3, 10, "INT2F has been hooked by someone else, cannot safely remove\n"));
+ fprintf(stderr, _(3, 9, "INT%X has been hooked by someone else, cannot safely remove\n"), 0x2F);
return false;
}
}
@@ -379,12 +569,28 @@ static bool check_if_driver_uninstallable(LPTSRDATA data)
static int unconfigure_driver(LPTSRDATA data)
{
+ if (data->port == 0) {
+ // PS/2 mode
+
+ // Turn off PS2 BIOS, and remove callback
+ ps2m_enable(false);
+ ps2m_set_callback(0);
+
#if USE_INTEGRATION
- set_integration(data, false);
+ set_integration(data, false, false);
#endif
+ } else {
+#if USE_SERIAL
+ // Serial mode
+ uint16_t iobase = data->port_io;
- ps2m_enable(false);
- ps2m_set_callback(0);
+ // Disable interrupts, and clear RTS/DTS, which should completely power down mouse
+ serial_configure_interrupts(iobase, 0);
+ serial_set_modem_control(iobase, 0);
+
+ // TODO Should I mask the serial IRQ on the PIC too?
+#endif
+ }
return 0;
}
@@ -393,6 +599,12 @@ static int uninstall_driver(LPTSRDATA data)
{
_dos_setvect(0x33, data->prev_int33_handler);
+#if USE_SERIAL
+ if (data->port_irq) {
+ _dos_setvect(0x8 + data->port_irq, data->prev_irq3_4_handler);
+ }
+#endif
+
#if USE_WIN386
_dos_setvect(0x2f, data->prev_int2f_handler);
#endif
@@ -409,7 +621,7 @@ static int uninstall_driver(LPTSRDATA data)
static int driver_reset(void)
{
printf(_(1, 19, "Reset mouse driver\n"));
- return int33_reset() == 0xFFFF;
+ return int33_reset() == INT33_MOUSE_FOUND;
}
static int driver_not_found(void)
@@ -476,6 +688,21 @@ static bool is_false(const char *s)
|| stricmp(s, "0") == 0;
}
+static int parse_port(const char *s)
+{
+ if (stricmp(s, "ps2") == 0) {
+ return 0;
+#if USE_SERIAL
+ } else if (strnicmp(s, "com", 3) == 0 && s[4] == '\0'
+ && s[3] >= '0' && s[3] <= '9') {
+ // "COMn" string
+ return s[3] - '0';
+#endif
+ } else {
+ return -1;
+ }
+}
+
int main(int argc, const char *argv[])
{
LPTSRDATA data = get_tsr_data(true);
@@ -487,6 +714,9 @@ int main(int argc, const char *argv[])
if (argi >= argc || stricmp(argv[argi], "install") == 0) {
bool high = true;
+ int portidx = 0, port;
+ // Default search order (PS2, COM1, COM2, COM3...)
+ int ports[1+MAX_PORTS] = {-1};
argi++;
for (; argi < argc; argi++) {
@@ -494,11 +724,25 @@ int main(int argc, const char *argv[])
high = false;
} else if (stricmp(argv[argi], "high") == 0) {
high = true;
+ } else if ((port = parse_port(argv[argi])) >= 0) {
+ if (portidx > MAX_PORTS) return invalid_arg(argv[argi]);
+ if (port < 0 || port > MAX_PORTS) return invalid_arg(argv[argi]);
+ ports[portidx++] = port;
} else {
return invalid_arg(argv[argi]);
}
}
+ if (!portidx) {
+ // User has not specified custom search order, so init with default
+ const unsigned num_com_ports = MIN(serial_num_ports(), MAX_PORTS);
+ if (ps2m_installed()) ports[portidx++] = 0; // 1st. PS2
+ // 2nd. COMn in order
+ for (port = 1; port <= num_com_ports; port++) {
+ ports[portidx++] = port;
+ }
+ }
+
if (data) {
printf(_(1, 21, "VBMouse already installed\n"));
print_help();
@@ -512,7 +756,7 @@ int main(int argc, const char *argv[])
} else {
deallocate_environment(_psp);
}
- err = configure_driver(data);
+ err = configure_driver(data, portidx, ports);
if (err) {
if (high) cancel_reallocation(FP_SEG(data));
return EXIT_FAILURE;
@@ -588,7 +832,7 @@ int main(int argc, const char *argv[])
if (is_false(argv[argi])) enable = false;
}
- return set_integration(data, enable);
+ return set_integration(data, enable, true);
} else if (stricmp(argv[argi], "hostcur") == 0) {
bool enable = true;
diff --git a/nls/vbmouse.en b/nls/vbmouse.en
index 3febaba..7272822 100644
--- a/nls/vbmouse.en
+++ b/nls/vbmouse.en
@@ -16,7 +16,7 @@
0.9: integ <ON|OFF> Enable/disable VirtualBox integration.
0.10: hostcur <ON|OFF> Enable/disable mouse cursor rendering in the host.
0.11: reset Reset mouse driver.
-1.0:Wheel mouse found and enabled\n
+1.0:Found PS/2 mouse with %u buttons, %u wheels\n
1.1:Setting wheel support to %s\n
1.2:enabled
1.3:disabled
@@ -38,6 +38,7 @@
1.19:Reset mouse driver\n
1.20:\nVBMouse %x.%x (like MSMOUSE %x.%x)\n
1.21:VBMouse already installed\n
+1.22:Found serial mouse on COM%u with %u buttons, %u wheels\n
3.0:Could not find PS/2 wheel mouse\n
3.1:Wheel not detected or support not enabled\n
3.2:Unknown key '%s'\n
@@ -47,8 +48,9 @@
3.6:Could not detect VMware, err=%ld\n
3.7:VMware absolute pointer error, err=0x%lx\n
3.8:Cannot init PS/2 mouse BIOS, err=%d\n
-3.9:INT33 has been hooked by someone else, cannot safely remove\n
-3.10:INT2F has been hooked by someone else, cannot safely remove\n
+3.9:INT%X has been hooked by someone else, cannot safely remove\n
3.11:Driver data not found (driver not installed?)\n
3.12:Invalid argument '%s'\n
3.13:Argument required for '%s'\n
+3.14:No mouse found\n
+3.15:Cannot find mouse in COM%u, err=%d\n
diff --git a/pic.h b/pic.h
new file mode 100644
index 0000000..0d87369
--- /dev/null
+++ b/pic.h
@@ -0,0 +1,59 @@
+/*
+ * VBMouse - Programmable Interrupt Controller routines
+ * Copyright (C) 2022 Javier S. Pedro
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef PIC_H
+#define PIC_H
+
+#include <conio.h>
+
+// TODO Currently do not support secondary PIC
+
+enum {
+ PIC1_CMD_IOPORT = 0x20,
+ PIC1_DATA_IOPORT = PIC1_CMD_IOPORT+1,
+ PIC2_CMD_IOPORT = 0xA0,
+ PIC2_DATA_IOPORT = PIC2_CMD_IOPORT+1,
+};
+
+enum PIC_CMDS {
+ /** End-Of-Interrupt command. */
+ PIC_CMD_EOI = 0x20
+};
+
+static void pic_mask_irq(unsigned irq)
+{
+ uint8_t mask = inp(PIC1_DATA_IOPORT);
+ mask |= (1 << irq);
+ outp(PIC1_DATA_IOPORT, mask);
+}
+
+static void pic_unmask_irq(unsigned irq)
+{
+ uint8_t mask = inp(PIC1_DATA_IOPORT);
+ mask &= ~(1 << irq);
+ outp(PIC1_DATA_IOPORT, mask);
+}
+
+static void pic_eoi_irq(unsigned irq)
+{
+ (void) irq;
+ outp(PIC1_CMD_IOPORT, PIC_CMD_EOI);
+}
+
+#endif // PIC_H
diff --git a/serial.h b/serial.h
index beeaf50..1ddc317 100644
--- a/serial.h
+++ b/serial.h
@@ -2,6 +2,7 @@
#define SERIAL_H
#include <stdint.h>
+#include <conio.h>
#include "bda.h"
@@ -12,6 +13,7 @@ enum SERIAL_UART_REGS {
SERIAL_DIVISOR_LO = 0,
/** (only with DLAB enabled) MSB of divisor/baud rate */
SERIAL_DIVISOR_HI = 1,
+ SERIAL_DIVISOR = SERIAL_DIVISOR_LO,
/** Interrupt Enable Register(read/write) */
SERIAL_IER = 1,
/** Fifo Control Register (write-only) */
@@ -45,6 +47,19 @@ enum SERIAL_DIVISOR {
SERIAL_DIVISOR_1200 = 96
};
+enum SERIAL_IER {
+ /** No interrupts */
+ SERIAL_IER_NONE = 0,
+ /** Enable Receiver Buffer Full Interrupt */
+ SERIAL_IER_ERBFI = 1 << 0,
+ /** Enable Transmitter Buffer Empty Interrupt */
+ SERIAL_IER_ETBEI = 1 << 1,
+ /** Enable Line Status Interrupt */
+ SERIAL_IER_ELSI = 1 << 2,
+ /** Enable Delta Status Signals Interrupt */
+ SERIAL_IER_EDSSI = 1 << 3,
+};
+
enum SERIAL_FCR {
SERIAL_FCR_RX_TRIGER_1 = 0 << 6,
SERIAL_FCR_RX_TRIGER_4 = 1 << 6,
@@ -54,6 +69,31 @@ enum SERIAL_FCR {
SERIAL_FCR_TX_RESET = 1 << 2,
SERIAL_FCR_RX_RESET = 1 << 1,
SERIAL_FCR_FIFO_ENABLE = 1 << 0,
+ SERIAL_FCR_FIFO_RESET = SERIAL_FCR_TX_RESET | SERIAL_FCR_RX_RESET,
+ SERIAL_FCR_FIFO_DISABLE = 0
+};
+
+enum SERIAL_IIR {
+ /** Mask for retrieving the interrupt that occured. See enum SERIAL_IID. */
+ SERIAL_IIR_ID = 0xF,
+
+ SERIAL_IIR_FIFO1 = 1 << 6,
+ SERIAL_IIR_FIFO2 = 1 << 7,
+};
+
+enum SERIAL_IID {
+ /** No interrupt. */
+ SERIAL_IID_NONE = 1,
+ /** Highest priority: line error. OE, PE, etc. Serviced by reading LSR. */
+ SERIAL_IID_ERROR = 6,
+ /** Second highest priority: data received or trigger reached. Serviced by reading data. */
+ SERIAL_IID_DATA = 4,
+ /** Second highest priority: stale data in FIFO. Serviced by reading data. */
+ SERIAL_IID_STALE = 0xC,
+ /** Third priority: transmitter buffer has room. Serviced by reading this reg/writing. */
+ SERIAL_IID_THRE = 2,
+ /** Fourth priority: MSR change. Serviced by reading MSR. */
+ SERIAL_IID_MODEM = 0,
};
enum SERIAL_LCR {
@@ -115,14 +155,14 @@ enum SERIAL_MSR {
struct serial_config
{
- uint8_t divisor_lo, divisor_hi;
+ uint16_t divisor;
uint8_t ier, lcr, mcr;
};
static unsigned serial_num_ports()
{
- uint8_t equipment = bda_get_byte(0x11);
- uint8_t numports = (equipment & 0xE) >> 1;
+ uint16_t equipment = bda_get_equipment();
+ uint8_t numports = (equipment & 0xE00) >> 9;
return numports;
}
@@ -131,11 +171,31 @@ static uint16_t serial_get_iobase(unsigned port)
uint16_t bdaport = bda_get_word((port - 1) * 2);
if (bdaport > 10) {
return bdaport;
- } else if (port == 1) {
- // Even if BIOS says no, hardcode the basic COM1 port at least.
+ }
+
+ // Even if BIOS says no, hardcode basic COM1/COM2 at least.
+ switch (port) {
+ case 1:
return 0x3F8;
+ case 2:
+ return 0x2F8;
+ default:
+ return 0;
+ }
+}
+
+static uint8_t serial_get_irq(unsigned port)
+{
+ switch (port) {
+ case 1:
+ case 3:
+ return 4;
+ case 2:
+ case 4:
+ return 3;
+ default:
+ return 0;
}
- return 0;
}
static void serial_save_config(unsigned iobase, struct serial_config *config)
@@ -145,8 +205,7 @@ static void serial_save_config(unsigned iobase, struct serial_config *config)
config->mcr = inp(iobase + SERIAL_MCR);
// Set DLAB so that we can read divisor
outp(iobase + SERIAL_LCR, config->lcr | SERIAL_LCR_DLAB);
- config->divisor_lo = inp(iobase + SERIAL_DIVISOR_LO);
- config->divisor_hi = inp(iobase + SERIAL_DIVISOR_HI);
+ config->divisor = inpw(iobase + SERIAL_DIVISOR);
// Restore DLAB to whatever it was
outp(iobase + SERIAL_LCR, config->lcr);
}
@@ -154,22 +213,34 @@ static void serial_save_config(unsigned iobase, struct serial_config *config)
static void serial_restore_config(unsigned iobase, const struct serial_config *config)
{
outp(iobase + SERIAL_LCR, config->lcr | SERIAL_LCR_DLAB);
- outp(iobase + SERIAL_DIVISOR_LO, config->divisor_lo);
- outp(iobase + SERIAL_DIVISOR_HI, config->divisor_hi);
+ outpw(iobase + SERIAL_DIVISOR, config->divisor);
outp(iobase + SERIAL_LCR, config->lcr);
outp(iobase + SERIAL_MCR, config->mcr);
outp(iobase + SERIAL_IER, config->ier);
}
-static void serial_configure(unsigned iobase, uint16_t divisor, uint8_t lcr)
+/// Configure line characteristics (speed, databytes, etc.)
+/// Disables interrupts
+static void serial_configure_line(unsigned iobase, uint16_t divisor, uint8_t lcr)
{
- outp(iobase + SERIAL_IER, 0); // Disable all interrupts
outp(iobase + SERIAL_LCR, SERIAL_LCR_DLAB); // Enable DLAB and clear all other flags
- outp(iobase + SERIAL_DIVISOR_LO, divisor);
- outp(iobase + SERIAL_DIVISOR_HI, divisor >> 8);
+ outpw(iobase + SERIAL_DIVISOR, divisor);
outp(iobase + SERIAL_LCR, lcr); // This also disables DLAB
- // Enable and reset both FIFOs
- outp(iobase + SERIAL_FCR, SERIAL_FCR_RX_TRIGER_14|SERIAL_FCR_FIFO_ENABLE|SERIAL_FCR_RX_RESET|SERIAL_FCR_TX_RESET);
+}
+
+/// Configures when exceptions are going to be raised
+/// it also clears all pending interrupts
+/// and enables/disables the OUT2 gator here, which I'm not sure if is conceptually clean
+static void serial_configure_interrupts(unsigned iobase, uint8_t ier)
+{
+ uint8_t mcr = inp(iobase + SERIAL_MCR);
+ outp(iobase + SERIAL_IER, ier);
+ if (ier) {
+ mcr |= SERIAL_MCR_OUT2; // Actually enables IRQ line for this UART
+ } else {
+ mcr &= ~SERIAL_MCR_OUT2;
+ }
+ outp(iobase + SERIAL_MCR, mcr);
}
static inline void serial_set_modem_control(unsigned iobase, uint8_t mcr)
@@ -177,9 +248,14 @@ static inline void serial_set_modem_control(unsigned iobase, uint8_t mcr)
outp(iobase + SERIAL_MCR, mcr);
}
-static inline void serial_set_modem_status(unsigned iobase, uint8_t msr)
+static inline void serial_set_fifo_control(unsigned iobase, uint8_t fcr)
{
- outp(iobase + SERIAL_MSR, msr);
+ outp(iobase + SERIAL_FCR, fcr);
+}
+
+static inline uint8_t serial_read_pending_interrupt(unsigned iobase)
+{
+ return inp(iobase + SERIAL_IIR) & SERIAL_IIR_ID;
}
static inline uint8_t serial_get_line_status(unsigned iobase)
@@ -187,6 +263,11 @@ static inline uint8_t serial_get_line_status(unsigned iobase)
return inp(iobase + SERIAL_LSR);
}
+static inline uint8_t serial_get_modem_status(unsigned iobase)
+{
+ return inp(iobase + SERIAL_MSR);
+}
+
static inline bool serial_data_ready(unsigned iobase)
{
return serial_get_line_status(iobase) & SERIAL_LSR_DR;
diff --git a/sermouse.h b/sermouse.h
new file mode 100644
index 0000000..a120f26
--- /dev/null
+++ b/sermouse.h
@@ -0,0 +1,38 @@
+/*
+ * VBMouse - Constants for serial mouse protocols
+ * Copyright (C) 2022 Javier S. Pedro
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef SERMOUSE_H
+#define SERMOUSE_H
+
+enum {
+ SERMOUSE_MS_BUTTON_LEFT = 1 << 5,
+ SERMOUSE_MS_BUTTON_RIGHT = 1 << 4,
+
+ SERMOUSE_MS_X_LO_MASK = 0x3F,
+ SERMOUSE_MS_Y_LO_MASK = 0x3F,
+
+ SERMOUSE_MS_X_HI_MASK = 0x3,
+ SERMOUSE_MS_X_HI_SHIFT = 6,
+
+ SERMOUSE_MS_Y_HI_MASK = 0xC,
+ SERMOUSE_MS_Y_HI_SHIFT = 4,
+
+};
+
+#endif // SERMOUSE_H
diff --git a/version.h b/version.h
index b756585..1df35fd 100644
--- a/version.h
+++ b/version.h
@@ -2,6 +2,6 @@
#define VERSION_H
#define VERSION_MAJOR 0
-#define VERSION_MINOR 0x70
+#define VERSION_MINOR 0x71
#endif // VERSION_H