aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--dlog.h66
-rw-r--r--dosmain.c138
-rw-r--r--dostsr.c220
-rw-r--r--dostsr.h31
-rw-r--r--makefile4
-rw-r--r--ps2.h8
-rw-r--r--vbox.h4
-rw-r--r--vboxlog.h12
-rw-r--r--vmware.h174
9 files changed, 575 insertions, 82 deletions
diff --git a/dlog.h b/dlog.h
index 6556089..3f853e7 100644
--- a/dlog.h
+++ b/dlog.h
@@ -20,16 +20,67 @@
#ifndef DLOG_H
#define DLOG_H
-#define ENABLE_DLOG 1
+#include <conio.h>
+
+// Customizable defines
+/** If 0, these routines become nops */
+#define ENABLE_DLOG 0
+/** 1 means target serial port, 0 means target IO port. */
+#define DLOG_TARGET_SERIAL 1
+/** 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). */
+#define DLOG_TARGET_PORT 0x3f8
+
+// End of customizable defines
#if ENABLE_DLOG
-#if 1
-#include "vboxlog.h"
-#define dlog_putc vbox_log_putc
-#endif
+/** Initializes the debug log port. */
+static void dlog_init();
+
+/** Logs a single character to the debug message IO port. */
+static inline void dlog_putc(char c);
+
+#if DLOG_TARGET_SERIAL
+
+static void dlog_init()
+{
+ // Comes straight from https://wiki.osdev.org/Serial_Ports#Initialization
+ outp(DLOG_TARGET_PORT + 1, 0x00); // Disable all interrupts
+ outp(DLOG_TARGET_PORT + 3, 0x80); // Enable DLAB (set baud rate divisor)
+ outp(DLOG_TARGET_PORT + 0, 0x03); // Set divisor to 3 (lo byte) 38400 baud
+ outp(DLOG_TARGET_PORT + 1, 0x00); // (hi byte)
+ outp(DLOG_TARGET_PORT + 3, 0x03); // 8 bits, no parity, one stop bit
+ outp(DLOG_TARGET_PORT + 2, 0xC7); // Enable FIFO, clear them, with 14-byte threshold
+ outp(DLOG_TARGET_PORT + 4, 0x03); // RTS/DSR set, IRQs disabled
+}
+
+static inline void dlog_putc(char c)
+{
+ while (!(inp(DLOG_TARGET_PORT + 5) & 0x20));
+ outp(DLOG_TARGET_PORT, c);
+}
+
+#else /* DLOG_TARGET_SERIAL */
-#define dlog_endline() dlog_putc('\n')
+static void dlog_init()
+{
+}
+
+static inline void dlog_putc(char c)
+{
+ outp(DLOG_TARGET_PORT, c);
+}
+
+
+
+#endif /* DLOG_TARGET_SERIAL */
+
+static void dlog_endline(void)
+{
+ dlog_putc('\n');
+}
/** Print string to log */
static void dlog_print(const char *s)
@@ -105,8 +156,9 @@ static void dlog_printd(int num)
dlog_printdb(num, 10);
}
-#else
+#else /* ENABLE_DLOG */
+#define dlog_init()
#define dlog_putc(c)
#define dlog_endline()
#define dlog_print(s)
diff --git a/dosmain.c b/dosmain.c
index 1e0b072..0ac5f75 100644
--- a/dosmain.c
+++ b/dosmain.c
@@ -27,6 +27,7 @@
#include "int33.h"
#include "ps2.h"
#include "vbox.h"
+#include "vmware.h"
#include "dostsr.h"
static void detect_wheel(LPTSRDATA data)
@@ -53,7 +54,7 @@ static int set_wheel(LPTSRDATA data, bool enable)
}
#if USE_VIRTUALBOX
-static int set_integration(LPTSRDATA data, bool enable)
+static int set_virtualbox_integration(LPTSRDATA data, bool enable)
{
if (enable) {
int err;
@@ -100,38 +101,141 @@ static int set_integration(LPTSRDATA data, bool enable)
return 0;
}
-static int set_host_cursor(LPTSRDATA data, bool enable)
+static int set_virtualbox_host_cursor(LPTSRDATA data, bool enable)
{
- if (data->vbavail) {
- printf("Setting host cursor to %s\n", enable ? "enabled" : "disabled");
- data->vbwantcursor = enable;
+ printf("Setting host cursor to %s\n", enable ? "enabled" : "disabled");
+ data->vbwantcursor = enable;
+
+ return 0;
+}
+#endif
+
+#if USE_VMWARE
+static int set_vmware_integration(LPTSRDATA data, bool enable)
+{
+ if (enable) {
+ int32_t version;
+ uint32_t status;
+ uint16_t data_avail;
+
+ data->vmwavail = false;
+
+ version = vmware_get_version();
+ if (version < 0) {
+ fprintf(stderr, "Could not detect VMware, err=%ld\n", version);
+ return -1;
+ }
+
+ printf("Found VMware protocol version %ld\n", version);
+
+ vmware_abspointer_cmd(VMWARE_ABSPOINTER_CMD_ENABLE);
+
+ status = vmware_abspointer_status();
+ if ((status & VMWARE_ABSPOINTER_STATUS_MASK_ERROR)
+ == VMWARE_ABSPOINTER_STATUS_MASK_ERROR) {
+ fprintf(stderr, "VMware absolute pointer error, err=0x%lx\n",
+ status & VMWARE_ABSPOINTER_STATUS_MASK_ERROR);
+ return -1;
+ }
+
+ vmware_abspointer_data_clear();
+
+ // TSR part will enable the absolute mouse when reset
+
+ printf("VMware integration enabled\n");
+ data->vmwavail = true;
} else {
- printf("VirtualBox integration is not available\n");
+ if (data->vmwavail) {
+ vmware_abspointer_cmd(VMWARE_ABSPOINTER_CMD_REQUEST_RELATIVE);
+ vmware_abspointer_cmd(VMWARE_ABSPOINTER_CMD_DISABLE);
+
+ data->vmwavail = false;
+ printf("Disabled VMware integration\n");
+ } else {
+ printf("VMware integration already disabled or not available\n");
+ }
}
return 0;
}
#endif
+static int set_integration(LPTSRDATA data, bool enable)
+{
+ if (enable) {
+ int err = -1;
+
+#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);
+ if (!err) return 0;
+#endif
+
+#if USE_VMWARE
+ // Afterwards try VMWare integration
+ err = set_vmware_integration(data, true);
+ if (!err) return 0;
+#endif
+
+ printf("Neither VirtualBox nor VMware integration available\n");
+ return err;
+ } else {
+#if USE_VIRTUALBOX
+ if (data->vbavail) {
+ set_virtualbox_integration(data, false);
+ }
+#endif
+#if USE_VMWARE
+ if (data->vmwavail) {
+ set_vmware_integration(data, false);
+ }
+#endif
+ return 0;
+ }
+}
+
+static int set_host_cursor(LPTSRDATA data, bool enable)
+{
+#if USE_VIRTUALBOX
+ if (data->vbavail) {
+ return set_virtualbox_host_cursor(data, enable);
+ }
+#endif
+ printf("VirtualBox integration not available\n");
+ return -1;
+}
+
static int configure_driver(LPTSRDATA data)
{
int err;
- // First check for PS/2 mouse availability
+ // Configure the debug logging port
+ dlog_init();
+
+ // Check for PS/2 mouse BIOS availability
if ((err = ps2m_init(PS2M_PACKET_SIZE_PLAIN))) {
fprintf(stderr, "Cannot init PS/2 mouse BIOS, err=%d\n", err);
// Can't do anything without PS/2
return err;
}
+#if USE_WHEEL
// Let's utilize the wheel by default
data->usewheel = true;
detect_wheel(data);
+#else
+ data->usewheel = false;
+#endif
+
+#if USE_INTEGRATION
+ // Enable integration by default
+ set_integration(data, true);
+#endif
#if USE_VIRTUALBOX
- // Assume initially that we want integration and host cursor
- set_integration(data, true);
- data->vbwantcursor = data->vbavail;
+ // Assume initially that we want host cursor
+ data->vbwantcursor = data->vbavail;
#endif
return 0;
@@ -156,7 +260,7 @@ static int install_driver(LPTSRDATA data)
data->prev_int33_handler = _dos_getvect(0x33);
_dos_setvect(0x33, int33_isr);
-#if USE_INT2F
+#if USE_WIN386
data->prev_int2f_handler = _dos_getvect(0x2f);
_dos_setvect(0x2f, int2f_isr);
#endif
@@ -177,7 +281,7 @@ static bool check_if_driver_uninstallable(LPTSRDATA data)
return true;
}
-#if USE_INT2F
+#if USE_WIN386
{
void (__interrupt __far *cur_int2f_handler)() = _dos_getvect(0x2f);
void (__interrupt __far *our_int2f_handler)() = MK_FP(FP_SEG(data), FP_OFF(int2f_isr));
@@ -210,7 +314,7 @@ static int uninstall_driver(LPTSRDATA data)
{
_dos_setvect(0x33, data->prev_int33_handler);
-#if USE_INT2F
+#if USE_WIN386
_dos_setvect(0x2f, data->prev_int2f_handler);
#endif
@@ -243,8 +347,10 @@ static void print_help(void)
"Supported actions:\n"
"\tinstall install the driver (default)\n"
"\tuninstall uninstall the driver from memory\n"
+#if USE_WHEEL
"\twheel <ON|OFF> enable/disable wheel API support\n"
-#if USE_VIRTUALBOX
+#endif
+#if USE_INTEGRATION
"\tinteg <ON|OFF> enable/disable virtualbox integration\n"
"\thostcur <ON|OFF> enable/disable mouse cursor rendering in host\n"
#endif
@@ -301,6 +407,7 @@ int main(int argc, const char *argv[])
return EXIT_FAILURE;
}
return uninstall_driver(data);
+#if USE_WHEEL
} else if (stricmp(argv[argi], "wheel") == 0) {
bool enable = true;
@@ -312,7 +419,8 @@ int main(int argc, const char *argv[])
}
return set_wheel(data, enable);
-#if USE_VIRTUALBOX
+#endif
+#if USE_INTEGRATION
} else if (stricmp(argv[argi], "integ") == 0) {
bool enable = true;
diff --git a/dostsr.c b/dostsr.c
index 0bed693..3d9066e 100644
--- a/dostsr.c
+++ b/dostsr.c
@@ -26,6 +26,8 @@
#include "int10vga.h"
#include "int2fwin.h"
#include "int33.h"
+#include "vbox.h"
+#include "vmware.h"
#include "dostsr.h"
#define MSB_MASK 0x8000U
@@ -303,7 +305,7 @@ static void refresh_cursor(void)
bool should_show = data.visible_count >= 0;
bool pos_changed, needs_refresh;
-#if USE_INT2F
+#if USE_WIN386
// Windows 386 is already rendering the cursor for us.
// Hide our own.
if (data.w386cursor) should_show = false;
@@ -646,58 +648,180 @@ static void handle_mouse_event(uint16_t buttons, bool absolute, int x, int y, in
}
/** PS/2 BIOS calls this routine to notify mouse events. */
-static void __far ps2_mouse_callback(uint8_t status, uint8_t x, uint8_t y, uint8_t z)
+static void __far ps2_mouse_handler(uint16_t word1, uint16_t word2, uint16_t word3, uint16_t word4)
{
-#pragma aux (PS2_CB) ps2_mouse_callback
+#pragma aux ps2_mouse_handler "*" parm caller [ax] [bx] [cx] [dx] modify [ax bx cx dx si di es fs gs]
- int sx = status & PS2M_STATUS_X_NEG ? 0xFF00 | x : x;
- int sy = -(status & PS2M_STATUS_Y_NEG ? 0xFF00 | y : y);
- int sz = z;
+ unsigned status;
+ int x, y, z;
bool abs = false;
#if TRACE_EVENTS
- dlog_print("ps2 callback status=");
- dlog_printx(status);
- dlog_print(" sx=");
- dlog_printd(sx);
- dlog_print(" sy=");
- dlog_printd(sy);
- dlog_print(" sz=");
- dlog_printd(z);
+ dlog_print("ps2 callback ");
+ dlog_printx(word1);
+ dlog_putc(' ');
+ dlog_printx(word2);
+ dlog_putc(' ');
+ dlog_printx(word3);
+ dlog_putc(' ');
+ dlog_printx(word4);
dlog_endline();
-#endif
+#endif /* TRACE_EVENTS */
+
+ // Decode the PS2 event args
+ // In a normal IBM PS/2 BIOS (incl. VirtualBox/Bochs/qemu/SeaBIOS):
+ // word1 low byte = status (following PS2M_STATUS_*)
+ // word2 low byte = x
+ // word3 low byte = y
+ // word4 = always zero
+ // In a PS/2 BIOS with wheel support (incl. VMware/DOSBox-X):
+ // taken from CuteMouse/KoKo:
+ // word1 high byte = x
+ // word1 low byte = status
+ // word2 low byte = y
+ // word3 low byte = z
+ // word4 = always zero
+ // VirtualBox/Bochs/qemu/SeaBIOS behave like a normal one,
+ // but they also store the raw contents of all mouse packets in the EBDA (starting 0x28 = packet 0).
+ // Other BIOSes don't do that so it is not a reliable option either.
+ // So, how to detect which BIOS we have?
+ // For now we are always assuming "normal" PS/2 BIOS.
+ // But with VirtualBox integration on we'll get the wheel packet from the EBDA,
+ // and with VMWare integration on we'll get it from the VMware protocol.
+ status = (uint8_t) word1;
+ x = (uint8_t) word2;
+ y = (uint8_t) word3;
+ z = 0;
+ (void) word4;
+
+ // 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);
#if USE_VIRTUALBOX
if (data.vbavail) {
uint16_t vbx, vby;
if ((vbox_get_mouse(&data.vb, &abs, &vbx, &vby) == 0) && abs) {
- sx = scaleu(vbx, 0xFFFFU, MAX(data.max.x, data.screen_max.x));
- sy = scaleu(vby, 0xFFFFU, MAX(data.max.y, data.screen_max.y));
+ // VirtualBox gives unsigned coordinates from 0...0xFFFFU,
+ // scale to 0..screen_size (in pixels).
+ // If the user is using a window larger than the screen, use it.
+ x = scaleu(vbx, 0xFFFFU, MAX(data.max.x, data.screen_max.x));
+ y = scaleu(vby, 0xFFFFU, MAX(data.max.y, data.screen_max.y));
data.vbhaveabs = true;
} else {
// VirtualBox does not support absolute coordinates,
// or user has disabled them.
data.vbhaveabs = false;
+ // Rely on PS/2 relative coordinates.
+ }
+
+ // VirtualBox/Bochs BIOS does not pass wheel data to the callback,
+ // so we will fetch it directly from the BIOS data segment.
+ if (data.haswheel) {
+ int8_t __far * mouse_packet = MK_FP(bda_get_ebda_segment(), 0x28);
+ z = mouse_packet[3];
}
}
+#endif /* USE_VIRTUALBOX */
+
+#if USE_VMWARE
+ if (data.vmwavail) {
+ uint32_t vmwstatus = vmware_abspointer_status();
+ uint16_t data_avail = vmwstatus & VMWARE_ABSPOINTER_STATUS_MASK_DATA;
+
+#if TRACE_EVENTS
+ dlog_print("vmware status=0x");
+ dlog_printx(vmwstatus >> 16);
+ dlog_print(" ");
+ dlog_printx(vmwstatus & 0xFFFF);
+ dlog_endline();
+#endif
+
+ if (data_avail >= VMWARE_ABSPOINTER_DATA_PACKET_SIZE) {
+ struct vmware_abspointer_data vmw;
+ vmware_abspointer_data(VMWARE_ABSPOINTER_DATA_PACKET_SIZE, &vmw);
+
+#if TRACE_EVENTS
+ dlog_print("vmware pstatus=0x");
+ dlog_printx(status);
+ dlog_print(" x=0x");
+ dlog_printx(vmw.x);
+ dlog_print(" z=");
+ dlog_printd((int8_t) (uint8_t) vmw.z);
+ dlog_endline();
#endif
- // VirtualBox/Bochs BIOS does not pass wheel data to the callback,
- // so we will fetch it directly from the BIOS data segment.
- if (data.haswheel && !sz) {
- int8_t __far * mouse_packet = MK_FP(bda_get_ebda_segment(), 0x28);
- sz = mouse_packet[3];
+ if (vmw.status & VMWARE_ABSPOINTER_STATUS_RELATIVE) {
+ x = (int16_t) vmw.x;
+ y = (int16_t) vmw.y;
+ z = (int8_t) (uint8_t) vmw.z;
+ } else {
+ abs = true;
+ x = scaleu(vmw.x & 0xFFFFU, 0xFFFFU,
+ MAX(data.max.x, data.screen_max.x));
+ y = scaleu(vmw.y & 0xFFFFU, 0xFFFFU,
+ MAX(data.max.y, data.screen_max.y));
+ z = (int8_t) (uint8_t) vmw.z;
+ }
+
+ if (vmw.status & VMWARE_ABSPOINTER_STATUS_BUTTON_LEFT) {
+ status |= PS2M_STATUS_BUTTON_1;
+ }
+ if (vmw.status & VMWARE_ABSPOINTER_STATUS_BUTTON_RIGHT) {
+ status |= PS2M_STATUS_BUTTON_2;
+ }
+ if (vmw.status & VMWARE_ABSPOINTER_STATUS_BUTTON_MIDDLE) {
+ status |= PS2M_STATUS_BUTTON_3;
+ }
+ } else {
+ return; // Ignore the PS/2 packet otherwise, it is likely garbage
+ }
}
+#endif /* USE_VMWARE */
handle_mouse_event(status & (PS2M_STATUS_BUTTON_1 | PS2M_STATUS_BUTTON_2 | PS2M_STATUS_BUTTON_3),
- abs, sx, sy, sz);
+ abs, x, y, z);
}
-#if USE_VIRTUALBOX
-static void enable_vbox_absolute(bool enable)
+void __declspec(naked) __far ps2_mouse_callback()
{
- data.vbhaveabs = false;
+ __asm {
+ pusha
+ push ds
+ push es
+ push fs
+ push gs
+ ; 8 + 4 saved registers, 24 bytes
+ ; plus 4 bytes for retf address
+ ; = 28 bytes of stack before callback args
+
+ mov bp, sp
+ push cs
+ pop ds
+
+ mov ax,[bp+28+6] ; Status
+ mov bx,[bp+28+4] ; X
+ mov cx,[bp+28+2] ; Y
+ mov dx,[bp+28+0] ; Z
+
+ call ps2_mouse_handler
+
+ pop gs
+ pop fs
+ pop es
+ pop ds
+ popa
+
+ retf
+ }
+}
+
+#if USE_INTEGRATION
+static void set_absolute(bool enable)
+{
+#if USE_VIRTUALBOX
+ data.vbhaveabs = false;
if (data.vbavail) {
int err = vbox_set_mouse(&data.vb, enable, false);
if (enable && !err) {
@@ -707,19 +831,34 @@ static void enable_vbox_absolute(bool enable)
dlog_puts("VBox absolute mouse disabled");
}
}
+#endif /* USE_VIRTUALBOX */
+#if USE_VMWARE
+ if (data.vmwavail) {
+ if (enable) {
+ uint16_t data_avail;
+ // It looks like a reset of the PS/2 mouse completely disables
+ // the vmware interface, so we have to reenable it from scratch.
+ vmware_abspointer_cmd(VMWARE_ABSPOINTER_CMD_ENABLE);
+ vmware_abspointer_data_clear();
+ vmware_abspointer_cmd(VMWARE_ABSPOINTER_CMD_REQUEST_ABSOLUTE);
+
+ dlog_puts("VMware absolute mouse enabled");
+ } else {
+ vmware_abspointer_cmd(VMWARE_ABSPOINTER_CMD_REQUEST_RELATIVE);
+ vmware_abspointer_cmd(VMWARE_ABSPOINTER_CMD_DISABLE);
+
+ dlog_puts("VMware absolute mouse disabled");
+ }
+ }
+#endif /* USE_VMWARE */
}
-#endif
+#endif /* USE_INTEGRATION */
static void reset_mouse_hardware()
{
ps2m_enable(false);
-#if USE_VIRTUALBOX
- // By default, enable the integration
- enable_vbox_absolute(true);
- load_cursor();
-#endif
-
+#if USE_WHEEL
if (data.usewheel && ps2m_detect_wheel()) {
// Detect wheel also reinitializes the mouse to the proper packet size
data.haswheel = true;
@@ -728,6 +867,10 @@ static void reset_mouse_hardware()
data.haswheel = false;
ps2m_init(PS2M_PACKET_SIZE_PLAIN);
}
+#else
+ data.haswheel = false;
+ ps2m_init(PS2M_PACKET_SIZE_PLAIN);
+#endif
ps2m_set_resolution(3); // 3 = 200 dpi, 8 counts per millimeter
ps2m_set_sample_rate(4); // 4 = 80 reports per second
@@ -735,6 +878,13 @@ static void reset_mouse_hardware()
ps2m_set_callback(ps2_mouse_callback);
+#if USE_INTEGRATION
+ // By default, enable absolute mouse
+ set_absolute(true);
+ // Reload hardware cursor
+ load_cursor();
+#endif
+
ps2m_enable(true);
}
@@ -813,7 +963,7 @@ static void return_clear_button_counter(union INTPACK __far *r, struct buttoncou
/** Entry point for our int33 API. */
static void int33_handler(union INTPACK r)
-#pragma aux int33_handler "*" parm caller [] modify [ax bx cx dx es]
+#pragma aux int33_handler "*" parm caller [] modify [ax bx cx dx si di es fs gs]
{
switch (r.x.ax) {
case INT33_RESET_MOUSE:
@@ -1062,7 +1212,7 @@ void __declspec(naked) __far int33_isr(void)
}
}
-#if USE_INT2F
+#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)
#pragma aux windows_mouse_handler "*" parm [ax] [bx] [cx] [dx] [si] modify [ax bx cx dx es]
diff --git a/dostsr.h b/dostsr.h
index 7f1200b..5aae98c 100644
--- a/dostsr.h
+++ b/dostsr.h
@@ -23,14 +23,26 @@
#include <stdbool.h>
#include <stdint.h>
-#include "vbox.h"
#include "int2fwin.h"
#include "int10vga.h"
+// User customizable defines
+
+/** Enable the VirtualBox integration */
#define USE_VIRTUALBOX 1
-#define USE_INT2F 1
+/** Enable the VMware integration */
+#define USE_VMWARE 1
+/** Enable the Windows 386/protected mode integration */
+#define USE_WIN386 1
+/** Enable the wheel. */
+#define USE_WHEEL 1
+/** Trace events verbosily */
#define TRACE_EVENTS 0
+// End of user customizable defines
+
+#define USE_INTEGRATION (USE_VIRTUALBOX || USE_VMWARE)
+
#define NUM_BUTTONS 3
#define GRAPHIC_CURSOR_WIDTH 16
@@ -40,10 +52,14 @@
#define GRAPHIC_CURSOR_DATA_LEN (2 * GRAPHIC_CURSOR_MASK_LEN)
#define VERSION_MAJOR 0
-#define VERSION_MINOR 3
+#define VERSION_MINOR 4
#define REPORTED_VERSION_MAJOR 8
#define REPORTED_VERSION_MINOR 0x20
+#if USE_VIRTUALBOX
+#include "vbox.h"
+#endif
+
struct point {
int16_t x, y;
};
@@ -52,7 +68,7 @@ typedef struct tsrdata {
// TSR installation data
/** Previous int33 ISR, storing it for uninstall. */
void (__interrupt __far *prev_int33_handler)();
-#if USE_INT2F
+#if USE_WIN386
void (__interrupt __far *prev_int2f_handler)();
#endif
/** Whether to enable & use wheel mouse. */
@@ -139,7 +155,7 @@ typedef struct tsrdata {
/** Events for which we should call the event handler. */
uint16_t event_mask;
-#if USE_INT2F
+#if USE_WIN386
/** Information that we pass to Windows 386 on startup. */
win386_startup_info w386_startup;
win386_instance_item w386_instance[2];
@@ -157,6 +173,11 @@ typedef struct tsrdata {
bool vbhaveabs : 1;
struct vboxcomm vb;
#endif
+
+#if USE_VMWARE
+ /** VMware is available. */
+ bool vmwavail : 1;
+#endif
} TSRDATA;
typedef TSRDATA * PTSRDATA;
diff --git a/makefile b/makefile
index f91b552..81c51cd 100644
--- a/makefile
+++ b/makefile
@@ -2,7 +2,7 @@
# Assuming you have sourced `owsetenv` beforehand.
#
dosobjs = dostsr.obj dosmain.obj vbox.obj
-doscflags = -bt=dos -ms -6 -os -oi -w3
+doscflags = -bt=dos -ms -6 -os -oi -w3 -wcd=202
# -ms to use small memory model (though sometimes ss != ds...)
# -os to optimize for size
# -oi to put intrinsics online (don't use them much)
@@ -11,7 +11,7 @@ dostsrcflags = -zu -s -g=RES_GROUP -nd=RES -nt=RES_TEXT -nc=RES_CODE
# -zu since ss != ds on the TSR
w16objs = w16mouse.obj
-w16cflags = -bt=windows -bd -mc -zu -s -6 -oi -w3
+w16cflags = -bt=windows -bd -mc -zu -s -6 -oi -w3 -wcd=202
# -bd to build DLL
# -mc to use compact memory model (far data pointers, ss != ds always)
# -zu for DLL calling convention (ss != ds)
diff --git a/ps2.h b/ps2.h
index 717ab84..ffcf7bb 100644
--- a/ps2.h
+++ b/ps2.h
@@ -80,12 +80,8 @@ enum ps2m_sample_rate {
PS2M_SAMPLE_RATE_200 = 6
};
-#pragma aux PS2_CB far loadds parm reverse caller []
-// TODO: ax and es look not be preserved with this. VBox BIOS already preserves them though, as well as 32-bit registers
-
-/** Invoked by the BIOS when there is a mouse event.
- * @param status combination of PS2M_STATUS_* flags */
-typedef void (__far * LPFN_PS2CALLBACK)(uint8_t status, uint8_t x, uint8_t y, uint8_t z);
+/** Invoked by the BIOS when there is a mouse event. */
+typedef void (__far * LPFN_PS2CALLBACK)();
static ps2m_err ps2m_init(uint8_t packet_size);
#pragma aux ps2m_init = \
diff --git a/vbox.h b/vbox.h
index 6bb8797..42b3c39 100644
--- a/vbox.h
+++ b/vbox.h
@@ -20,6 +20,8 @@
#ifndef VBOX_H
#define VBOX_H
+#pragma off (unreferenced)
+
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
@@ -155,4 +157,6 @@ static inline unsigned vbox_req_mouse_pointer_size(unsigned width, unsigned heig
return MAX(sizeof(VMMDevReqMousePointer), 24 + 20 + data_size);
}
+#pragma pop (unreferenced)
+
#endif
diff --git a/vboxlog.h b/vboxlog.h
deleted file mode 100644
index 559566b..0000000
--- a/vboxlog.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef VBOXDLOG_H
-#define VBOXDLOG_H
-
-#include <conio.h>
-
-/** Logs a single character to the VBox debug message port. */
-static inline void vbox_log_putc(char c)
-{
- outp(0x504, c);
-}
-
-#endif // VBOXDLOG_H
diff --git a/vmware.h b/vmware.h
new file mode 100644
index 0000000..8cb8f0f
--- /dev/null
+++ b/vmware.h
@@ -0,0 +1,174 @@
+/*
+ * VBMouse - VMware communication 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 VMWARE_H
+#define VMWARE_H
+
+#include <stdint.h>
+
+/* Ideas from https://wiki.osdev.org/VMware_tools */
+
+#define VMWARE_MAGIC 0x564D5868UL
+#define VMWARE_PORT 0x5658
+
+enum vmware_cmds {
+ VMWARE_CMD_GETVERSION = 10,
+ VMWARE_CMD_ABSPOINTER_DATA = 39,
+ VMWARE_CMD_ABSPOINTER_STATUS = 40,
+ VMWARE_CMD_ABSPOINTER_COMMAND = 41
+};
+
+enum vmware_abspointer_cmds {
+ VMWARE_ABSPOINTER_CMD_ENABLE = 0x45414552UL,
+ VMWARE_ABSPOINTER_CMD_DISABLE = 0x000000f5UL,
+ VMWARE_ABSPOINTER_CMD_REQUEST_RELATIVE = 0x4c455252UL,
+ VMWARE_ABSPOINTER_CMD_REQUEST_ABSOLUTE = 0x53424152UL,
+};
+
+enum vmware_abspointer_status {
+ VMWARE_ABSPOINTER_STATUS_MASK_ERROR = 0xFFFF0000UL,
+ VMWARE_ABSPOINTER_STATUS_MASK_DATA = 0x0000FFFFUL
+};
+
+enum vmware_abspointer_data_status {
+ VMWARE_ABSPOINTER_STATUS_RELATIVE = 0x00010000U,
+ VMWARE_ABSPOINTER_STATUS_BUTTON_LEFT = 0x20,
+ VMWARE_ABSPOINTER_STATUS_BUTTON_RIGHT = 0x10,
+ VMWARE_ABSPOINTER_STATUS_BUTTON_MIDDLE = 0x08,
+};
+
+struct vmware_call_regs {
+ union {
+ uint32_t eax;
+ uint32_t magic;
+ };
+ union {
+ uint32_t ebx;
+ uint32_t size;
+ };
+ union {
+ uint32_t ecx;
+ uint32_t command;
+ };
+ union {
+ uint32_t edx;
+ uint32_t port;
+ };
+};
+
+struct vmware_abspointer_data {
+ uint32_t status;
+ int32_t x;
+ int32_t y;
+ int32_t z;
+};
+
+#define VMWARE_ABSPOINTER_DATA_PACKET_SIZE 4
+
+static inline void vmware_call(struct vmware_call_regs __far * regs);
+#pragma aux vmware_call = \
+ "pushad" /* First make a copy of all 32-bit registers, to avoid clobbering them. */ \
+ "mov eax, es:[di + 4*0]" \
+ "mov ebx, es:[di + 4*1]" \
+ "mov ecx, es:[di + 4*2]" \
+ "mov edx, es:[di + 4*3]" \
+ "in eax, dx" /* This performs the actual backdoor call. */ \
+ "mov es:[di + 4*0], eax" \
+ "mov es:[di + 4*1], ebx" \
+ "mov es:[di + 4*2], ecx" \
+ "mov es:[di + 4*3], edx" \
+ "popad" \
+ __parm [es di] \
+ __modify []
+
+/** Checks the presence of the VMware backdoor by comparing magic numbers.
+ * @return detected version of the vmware protocol (0-6), -1 if not detected. */
+static int32_t vmware_get_version(void)
+{
+ struct vmware_call_regs regs;
+ regs.magic = VMWARE_MAGIC;
+ regs.port = VMWARE_PORT;
+ regs.command = VMWARE_CMD_GETVERSION;
+ regs.ebx = ~VMWARE_MAGIC;
+ vmware_call(&regs);
+ if (regs.ebx != VMWARE_MAGIC) {
+ return -1;
+ }
+ return regs.eax;
+}
+
+/** Sends a command to the VMware absolute pointing interface.
+ * @param cmd see VMWARE_ABSPOINTER_CMD_*. */
+static void vmware_abspointer_cmd(uint32_t cmd)
+{
+ struct vmware_call_regs regs;
+ regs.magic = VMWARE_MAGIC;
+ regs.port = VMWARE_PORT;
+ regs.command = VMWARE_CMD_ABSPOINTER_COMMAND;
+ regs.ebx = cmd;
+ vmware_call(&regs);
+}
+
+/** Gets the status reg from the VMware absolute pointing interface.
+ * @return status
+ * VMWARE_ABSPOINTER_STATUS_MASK_ERROR bits: error flags.
+ * VMWARE_ABSPOINTER_STATUS_MASK_DATA bits: amount of data available to read. */
+static uint32_t vmware_abspointer_status(void)
+{
+ struct vmware_call_regs regs;
+ regs.magic = VMWARE_MAGIC;
+ regs.port = VMWARE_PORT;
+ regs.command = VMWARE_CMD_ABSPOINTER_STATUS;
+ regs.ebx = 0;
+ vmware_call(&regs);
+ return regs.eax;
+}
+
+/** Reads data from the VMware absolute pointing interface.
+ * @param size amount of data to read (value between 1 and 4)
+ * ensure it is less than the amount of data available,
+ * qemu disconnects us if we read more than we can.
+ * @return status
+ * VMWARE_ABSPOINTER_STATUS_MASK_ERROR bits: error flags.
+ * VMWARE_ABSPOINTER_STATUS_MASK_DATA bits: amount of data available to read. */
+static void vmware_abspointer_data(unsigned size, struct vmware_abspointer_data __far * data)
+{
+ union u {
+ struct vmware_call_regs regs;
+ struct vmware_abspointer_data data;
+ } __far *p = (union u __far *) data;
+ p->regs.magic = VMWARE_MAGIC;
+ p->regs.port = VMWARE_PORT;
+ p->regs.command = VMWARE_CMD_ABSPOINTER_DATA;
+ p->regs.size = size;
+ vmware_call(&p->regs);
+}
+
+/** Reads (and discards) all available data in the VMware absolute pointing interface. */
+static void vmware_abspointer_data_clear(void)
+{
+ uint16_t data_avail;
+ // Drop any data in the queue
+ while ((data_avail = vmware_abspointer_status())) {
+ struct vmware_abspointer_data data;
+ vmware_abspointer_data(MIN(4, data_avail), &data);
+ }
+}
+
+#endif // VMWARE_H