aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJavier <dev.git@javispedro.com>2022-03-29 01:15:53 +0200
committerJavier <dev.git@javispedro.com>2022-03-29 01:15:53 +0200
commita816d1a09b1045fb5c155ac73f3231fcf9d93180 (patch)
treec4e31e850b9f2afb36acd6119483cf350c33f596
parent67ebca92621aef31ff97705013456e95e60f7fbe (diff)
downloadvbados-a816d1a09b1045fb5c155ac73f3231fcf9d93180.tar.gz
vbados-a816d1a09b1045fb5c155ac73f3231fcf9d93180.zip
initial import of DOS mouse driver
-rw-r--r--.gitignore4
-rw-r--r--dlog.h87
-rw-r--r--dosmain.c275
-rw-r--r--dosmouse.lnk9
-rw-r--r--dostsr.c740
-rw-r--r--dostsr.h110
-rw-r--r--int10vga.h41
-rw-r--r--int33.h197
-rw-r--r--makefile45
-rw-r--r--pci.h8
-rw-r--r--ps2.h17
-rw-r--r--utils.h94
-rw-r--r--vbmouse.lnk9
-rw-r--r--vbox.c243
-rw-r--r--vbox.h175
-rw-r--r--vboxdev.h13
-rw-r--r--w16mouse.c (renamed from mousew16.c)179
-rw-r--r--w16mouse.h (renamed from mousew16.h)0
-rw-r--r--w16mouse.lnk11
19 files changed, 1854 insertions, 403 deletions
diff --git a/.gitignore b/.gitignore
index 0fd4460..ad3c864 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,7 @@
vbmouse.drv
-vbmouse.map
+vbmouse.exe
vbmouse.flp
*.o
+*.obj
+*.map
runvm.sh
diff --git a/dlog.h b/dlog.h
new file mode 100644
index 0000000..dfa346d
--- /dev/null
+++ b/dlog.h
@@ -0,0 +1,87 @@
+#ifndef DLOG_H
+#define DLOG_H
+
+#define ENABLE_DLOG 1
+
+#if ENABLE_DLOG
+
+#if 1
+#include "vbox.h"
+#define dlog_putc vbox_log_putc
+#endif
+
+static inline void dlog_endline(void)
+{
+ dlog_putc('\n');
+}
+
+static inline void dlog_print(const char *s)
+{
+ char c;
+ while (c = *s++) {
+ dlog_putc(c);
+ }
+}
+
+static inline void dlog_printu(unsigned int num, int base)
+{
+ char buf[4];
+ int i = 0;
+
+ do {
+ int digit = num % base;
+
+ if (digit < 10) {
+ buf[i] = '0' + digit;
+ } else {
+ buf[i] = 'a' + (digit - 10);
+ }
+
+ i++;
+ num /= base;
+ } while (num > 0);
+
+ while (i--) {
+ dlog_putc(buf[i]);
+ }
+}
+
+static inline void dlog_printx(unsigned int num)
+{
+ dlog_printu(num, 16);
+}
+
+static inline void dlog_printd(int num, int base)
+{
+ unsigned int unum;
+
+ // TODO
+ if (num < 0) {
+ dlog_putc('-');
+ unum = -num;
+ } else {
+ unum = num;
+ }
+
+ dlog_printu(unum, base);
+}
+
+static inline void dlog_puts(const char *s)
+{
+ dlog_print(s);
+ dlog_endline();
+}
+
+#else
+
+#define dlog_putc(c)
+#define dlog_endline()
+#define dlog_print(s)
+#define dlog_printu(n)
+#define dlog_printx(n)
+#define dlog_printd(n,b)
+#define dlog_puts(s)
+
+#endif
+
+#endif
diff --git a/dosmain.c b/dosmain.c
new file mode 100644
index 0000000..36bea69
--- /dev/null
+++ b/dosmain.c
@@ -0,0 +1,275 @@
+/*
+ * VBMouse - DOS mouse driver exec entry point
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dos.h>
+#include <i86.h>
+
+#include "dlog.h"
+#include "ps2.h"
+#include "vbox.h"
+#include "dostsr.h"
+
+static unsigned get_resident_size(void)
+{
+ return FP_OFF(&resident_end);
+}
+
+static int set_integration(LPTSRDATA data, bool enable)
+{
+ if (enable) {
+ int err;
+
+ data->vbavail = false; // Reinitialize it even if already enabled
+
+ if ((err = vbox_init(&data->vb)) == 0) {
+ printf("Found VirtualBox device at IO 0x%x\n", data->vb.iobase);
+ printf("Found physical address for VBox communication 0x%lx\n", data->vb.buf_physaddr);
+
+ if ((err = vbox_report_guest_info(&data->vb, VBOXOSTYPE_DOS)) == 0) {
+ printf("VirtualBox integration enabled\n");
+ data->vbavail = true;
+ } else {
+ fprintf(stderr, "VirtualBox communication is not working, err=%d\n", err);
+ return err;
+ }
+ } else {
+ fprintf(stderr, "Cannot find VirtualBox PCI device, err=%d\n", err);
+ return err;
+ }
+ } else {
+ if (data->vbavail) {
+ vbox_set_mouse(&data->vb, false, false);
+ printf("Disabled VirtualBox integration\n");
+ data->vbavail = false;
+ } else {
+ printf("VirtualBox already disabled or not available\n");
+ }
+ }
+
+ return 0;
+}
+
+static int set_host_cursor(LPTSRDATA data, bool enable)
+{
+#if USE_VIRTUALBOX
+ if (data->vbavail) {
+ printf("Setting host cursor to %s\n", enable ? "enabled" : "disabled");
+ data->vbwantcursor = enable;
+ } else {
+ printf("VirtualBox integration is not available\n");
+ }
+#endif
+ return EXIT_SUCCESS;
+}
+
+static int configure_driver(LPTSRDATA data)
+{
+ int err;
+
+ // First check for PS/2 mouse availability
+ if ((err = ps2m_init(PS2_MOUSE_PLAIN_PACKET_SIZE))) {
+ fprintf(stderr, "Cannot init PS/2 mouse BIOS, err=%d\n", err);
+ // Can't do anything without PS/2
+ return err;
+ }
+
+#if USE_VIRTUALBOX
+ // Assume initially that we want integration and host cursor
+ set_integration(data, true);
+ data->vbwantcursor = data->vbavail;
+#endif
+
+ return 0;
+}
+
+static void deallocate_environment(uint16_t psp)
+{
+ // TODO : Too lazy to make PSP struct;
+ // 0x2C is offsetof the environment block field on the PSP
+ uint16_t __far *envblockP = (uint16_t __far *) MK_FP(psp, 0x2C);
+ _dos_freemem(*envblockP);
+ *envblockP = 0;
+}
+
+static int install_driver(LPTSRDATA data)
+{
+ unsigned int resident_size = get_resident_size();
+
+ // Not that this will do anything other than fragment memory, but why not...
+ deallocate_environment(_psp);
+
+ data->prev_int33_handler = _dos_getvect(0x33);
+
+ _dos_setvect(0x33, int33_isr);
+
+ _dos_keep(EXIT_SUCCESS, (256 + resident_size + 15) / 16);
+ return 0;
+}
+
+static bool check_if_driver_uninstallable(LPTSRDATA data)
+{
+ void (__interrupt __far *cur_int33_handler)() = _dos_getvect(0x33);
+ void (__interrupt __far *our_int33_handler)() = MK_FP(FP_SEG(data), FP_OFF(int33_isr));
+
+ if (cur_int33_handler != our_int33_handler) {
+ fprintf(stderr, "INT33 has been hooked by some other driver, removing anyway\n");
+ return true;
+ }
+
+ return true;
+}
+
+static int unconfigure_driver(LPTSRDATA data)
+{
+#if USE_VIRTUALBOX
+ if (data->vbavail) {
+ set_integration(data, false);
+ }
+#endif
+
+ ps2m_enable(false);
+ ps2m_set_callback(0);
+
+ return 0;
+}
+
+static int uninstall_driver(LPTSRDATA data)
+{
+ _dos_setvect(0x33, data->prev_int33_handler);
+
+ // Find and deallocate the PSP (including the entire program),
+ // it is always 256 bytes (16 paragraphs) before the TSR segment
+ _dos_freemem(FP_SEG(data) - 16);
+
+ printf("Driver uninstalled\n");
+
+ return 0;
+}
+
+static unsigned int33_call(unsigned ax);
+#pragma aux int33_call parm [ax] value [ax] = \
+ "int 0x33";
+
+static int driver_reset(void)
+{
+ printf("Reset mouse driver\n");
+ return int33_call(0x0) == 0xFFFF;
+}
+
+static int driver_not_found(void)
+{
+ fprintf(stderr, "Driver data not found (driver not installed?)\n");
+ return EXIT_FAILURE;
+}
+
+static void print_help(void)
+{
+ printf("\n"
+ "Usage: \n"
+ "\tVBMOUSE <ACTION>\n\n"
+ "Supported actions:\n"
+ "\tinstall install the driver (default)\n"
+ "\tuninstall uninstall the driver from memory\n"
+ "\tinteg <ON|OFF> enable/disable virtualbox integration\n"
+ "\thostcur <ON|OFF> enable/disable mouse cursor rendering in host\n"
+ "\treset reset mouse driver settings\n"
+ );
+}
+
+static bool is_true(const char *s)
+{
+ return stricmp(s, "yes") == 0
+ || stricmp(s, "y") == 0
+ || stricmp(s, "on") == 0
+ || stricmp(s, "true") == 0
+ || stricmp(s, "enabled") == 0
+ || stricmp(s, "enable") == 0
+ || stricmp(s, "1") == 0;
+}
+
+static bool is_false(const char *s)
+{
+ return stricmp(s, "no") == 0
+ || stricmp(s, "n") == 0
+ || stricmp(s, "off") == 0
+ || stricmp(s, "false") == 0
+ || stricmp(s, "disabled") == 0
+ || stricmp(s, "disable") == 0
+ || stricmp(s, "0") == 0;
+}
+
+int main(int argc, const char *argv[])
+{
+ LPTSRDATA data = get_tsr_data(true);
+ int err, argi = 1;
+
+ printf("VBMouse %d.%d\n", DRIVER_VERSION_MAJOR, DRIVER_VERSION_MINOR);
+
+ if (argi >= argc || stricmp(argv[argi], "install") == 0) {
+ if (data) {
+ printf("VBMouse already installed\n");
+ return EXIT_SUCCESS;
+ }
+
+ data = get_tsr_data(false);
+ err = configure_driver(data);
+ if (err) return EXIT_FAILURE;
+ return install_driver(data);
+ } else if (stricmp(argv[argi], "uninstall") == 0) {
+ if (!data) return driver_not_found();
+ if (!check_if_driver_uninstallable(data)) {
+ return EXIT_FAILURE;
+ }
+ err = unconfigure_driver(data);
+ if (err) {
+ return EXIT_FAILURE;
+ }
+ return uninstall_driver(data);
+ } else if (stricmp(argv[argi], "integ") == 0) {
+ bool enable = true;
+
+ if (!data) return driver_not_found();
+
+ argi++;
+ if (argi < argc) {
+ if (is_false(argv[argi])) enable = false;
+ }
+
+ return set_integration(data, enable);
+ } else if (stricmp(argv[argi], "hostcur") == 0) {
+ bool enable = true;
+
+ if (!data) return driver_not_found();
+
+ argi++;
+ if (argi < argc) {
+ if (is_false(argv[argi])) enable = false;
+ }
+
+ return set_host_cursor(data, enable);
+ } else if (stricmp(argv[argi], "reset") == 0) {
+ return driver_reset();
+ }
+
+ print_help();
+ return EXIT_FAILURE;
+}
diff --git a/dosmouse.lnk b/dosmouse.lnk
new file mode 100644
index 0000000..bc1596f
--- /dev/null
+++ b/dosmouse.lnk
@@ -0,0 +1,9 @@
+system dos
+option map=dosmouse.map
+# Put the resident text & data first, then the rest of standard classses
+order clname RES_CODE
+ clname FAR_DATA
+ clname CODE segment BEGTEXT segment _TEXT
+ clname BEGDATA
+ clname DATA
+ clname BSS
diff --git a/dostsr.c b/dostsr.c
new file mode 100644
index 0000000..b4d517a
--- /dev/null
+++ b/dostsr.c
@@ -0,0 +1,740 @@
+/*
+ * VBMouse - DOS mouse driver resident part
+ * 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.
+ */
+
+#include <i86.h>
+
+#include "dlog.h"
+#include "ps2.h"
+#include "int10vga.h"
+#include "int33.h"
+#include "dostsr.h"
+
+TSRDATA data;
+
+static const uint16_t default_cursor_graphic[] = {
+ 0x3FFF, 0x1FFF, 0x0FFF, 0x07FF,
+ 0x03FF, 0x01FF, 0x00FF, 0x007F,
+ 0x003F, 0x001F, 0x01FF, 0x00FF,
+ 0x30FF, 0xF87F, 0xF87F, 0xFCFF,
+ 0x0000, 0x4000, 0x6000, 0x7000,
+ 0x7800, 0x7C00, 0x7E00, 0x7F00,
+ 0x7F80, 0x7C00, 0x6C00, 0x4600,
+ 0x0600, 0x0300, 0x0300, 0x0000
+};
+
+/** This is going to end up at offset 0 of our segment,
+ * have something here in case we end up calling a NULL near pointer by mistake. */
+void tsr_null(void)
+{
+ breakpoint();
+}
+
+static void bound_position_to_window(void)
+{
+ if (data.pos.x < data.min.x) data.pos.x = data.min.x;
+ if (data.pos.x > data.max.x) data.pos.x = data.max.x;
+ if (data.pos.y < data.min.y) data.pos.y = data.min.y;
+ if (data.pos.y > data.max.y) data.pos.y = data.max.y;
+}
+
+/** Refreshes cursor position and visibility. */
+static void refresh_cursor(void)
+{
+ bool should_show = data.visible_count >= 0;
+
+#if USE_VIRTUALBOX
+ if (data.vbwantcursor) {
+ int err = 0;
+ if (should_show != data.cursor_visible) {
+ int err = vbox_set_pointer_visible(&data.vb, should_show);
+ if (err == 0 && data.vbhaveabs) {
+ data.cursor_visible = should_show;
+ }
+ }
+ if (err == 0 & data.vbhaveabs) {
+ // No need to show the cursor; VirtualBox is already showing it for us.
+ return;
+ }
+ }
+#endif
+
+ if (data.screen_text_mode) {
+ if (data.cursor_visible) {
+ // Restore the character under the old position of the cursor
+ uint16_t __far *ch = get_video_char(data.screen_page,
+ data.cursor_pos.x / 8, data.cursor_pos.y / 8);
+ *ch = data.cursor_prev_char;
+ data.cursor_visible = false;
+ }
+
+ if (should_show) {
+ uint16_t __far *ch = get_video_char(data.screen_page,
+ data.pos.x / 8, data.pos.y / 8);
+ data.cursor_prev_char = *ch;
+ data.cursor_pos = data.pos;
+ *ch = (*ch & data.cursor_text_and_mask) ^ data.cursor_text_xor_mask;
+ data.cursor_visible = true;
+ }
+ } else {
+ dlog_puts("Graphic mode cursor is not implemented");
+ }
+}
+
+static void load_cursor(void)
+{
+#if USE_VIRTUALBOX
+ if (data.vbwantcursor) {
+ VMMDevReqMousePointer *req = (VMMDevReqMousePointer *) data.vb.buf;
+ const unsigned width = 16, height = 16;
+ unsigned and_mask_size = (width + 7) / 8 * height;
+ unsigned xor_mask_size = width * height * 4;
+ unsigned data_size = and_mask_size + xor_mask_size;
+ unsigned full_size = MAX(sizeof(VMMDevReqMousePointer), 24 + 20 + data_size);
+ unsigned int offset = 0, y, x;
+
+ bzero(req, full_size);
+
+ req->header.size = full_size;
+ req->header.version = VMMDEV_REQUEST_HEADER_VERSION;
+ req->header.requestType = VMMDevReq_SetPointerShape;
+ req->header.rc = -1;
+
+ req->fFlags = VBOX_MOUSE_POINTER_SHAPE;
+ req->xHot = BOUND(data.cursor_hotspot.x, 0, width);
+ req->yHot = BOUND(data.cursor_hotspot.y, 0, height);
+ req->width = width;
+ req->height = height;
+
+ // Just byteswap the and mask
+ for (y = 0; y < height; ++y) {
+ uint16_t line = data.cursor_graphic[y];
+ req->pointerData[(y*2)] = (line >> 8) & 0xFF;
+ req->pointerData[(y*2)+1] = line & 0xFF;
+ }
+ offset += and_mask_size;
+
+ // Store the XOR mask in "RGBA" format.
+ for (y = 0; y < height; ++y) {
+ uint16_t line = data.cursor_graphic[height + y];
+
+ for (x = 0; x < width; ++x) {
+ unsigned int pos = offset + (y * width * 4) + (x*4) + 0;
+ uint8_t val = (line & 0x8000) ? 0xFF : 0;
+
+ req->pointerData[pos + 0] = val;
+ req->pointerData[pos + 1] = val;
+ req->pointerData[pos + 2] = val;
+ req->pointerData[pos + 3] = 0;
+
+ line <<= 1;
+ }
+ }
+
+ dlog_puts("Loading cursor to VBox");
+
+ vbox_send_request(data.vb.iobase, data.vb.buf_physaddr);
+
+ if (req->header.rc != 0) {
+ dlog_puts("Could not send cursor to VirtualBox");
+ }
+
+ // VirtualBox shows the cursor even if we don't want;
+ // rehide if necessary.
+ data.cursor_visible = true;
+ refresh_cursor();
+ }
+#endif
+}
+
+static void refresh_video_info(void)
+{
+ uint8_t screen_columns;
+ uint8_t video_page;
+ uint8_t mode = int10_get_video_mode(&screen_columns, &video_page);
+ bool mode_change = mode != data.screen_mode;
+
+ if (mode_change && data.cursor_visible) {
+ // Assume cursor is lost
+ data.cursor_visible = false;
+ }
+
+ dlog_print("Current video mode=");
+ dlog_printx(mode);
+ dlog_print(" with cols=");
+ dlog_printd(screen_columns, 10);
+ dlog_endline();
+
+ data.screen_mode = mode;
+ data.screen_page = video_page;
+
+ switch (mode) {
+ case 0:
+ case 1:
+ case 2:
+ case 3: /* VGA text modes with 25 rows and variable columns */
+ data.screen_max.x = (screen_columns * 8) - 1;
+ data.screen_max.y = (25 * 8) - 1;
+ data.screen_text_mode = true;
+ break;
+
+ case 4:
+ case 5:
+ case 6: /* Graphic CGA modes */
+ data.screen_max.x = 640 - 1;
+ data.screen_max.y = 200 - 1;
+ data.screen_text_mode = false;
+ break;
+
+ case 0x11:
+ case 0x12:
+ case 0x13: /* Graphical 640x480 modes. */
+ data.screen_max.x = 640 - 1;
+ data.screen_max.y = 480 - 1;
+ data.screen_text_mode = false;
+ break;
+
+ default:
+ data.screen_max.x = 0;
+ data.screen_max.y = 0;
+ data.screen_text_mode = false;
+ break;
+ }
+
+ dlog_print(" screen_x=");
+ dlog_printd(data.screen_max.x, 10);
+ dlog_print(" y=");
+ dlog_printd(data.screen_max.y, 10);
+ dlog_endline();
+}
+
+static void call_event_handler(void (__far *handler)(), uint16_t events,
+ uint16_t buttons, int16_t x, int16_t y,
+ int16_t delta_x, int16_t delta_y)
+{
+#if TOO_VERBOSE
+ dlog_print("calling event handler events=");
+ dlog_printx(events);
+ dlog_print(" buttons=");
+ dlog_printx(buttons);
+ dlog_print(" x=");
+ dlog_printd(x, 10);
+ dlog_print(" y=");
+ dlog_printd(y, 10);
+ dlog_print(" dx=");
+ dlog_printd(delta_x, 10);
+ dlog_print(" dy=");
+ dlog_printd(delta_y, 10);
+ dlog_endline();
+#endif
+
+ __asm {
+ mov ax, [events]
+ mov bx, [buttons]
+ mov cx, [x]
+ mov dx, [y]
+ mov si, [delta_x]
+ mov di, [delta_y]
+
+ call dword ptr [handler]
+ }
+}
+
+static void handle_mouse_event(uint8_t buttons, bool absolute, int x, int y, int z)
+{
+ uint16_t events = 0;
+ int i;
+
+#if TOO_VERBOSE
+ dlog_print("handle mouse event");
+ if (absolute) dlog_print(" absolute");
+ dlog_print(" buttons=");
+ dlog_printx(buttons);
+ dlog_print(" x=");
+ dlog_printd(x, 10);
+ dlog_print(" y=");
+ dlog_printd(y, 10);
+ dlog_endline();
+#endif
+
+ if (absolute) {
+ // Absolute movement: x,y are in screen pixels units
+ // Translate to mickeys for delta movement
+ // TODO: we are not storing the remainder here (e.g. delta_frac),
+ // but does anyone care about it ?
+ data.delta.x += ((x - data.pos.x) * 8) / data.mickeysPerLine.x;
+ data.delta.y += ((y - data.pos.y) * 8) / data.mickeysPerLine.y;
+
+ // Store the new absolute position
+ data.pos.x = x;
+ data.pos.y = y;
+ data.pos_frac.x = 0;
+ data.pos_frac.y = 0;
+ } else {
+ // Relative movement: x,y are in mickeys
+ uint16_t ticks = bda_get_tick_count_lo();
+ unsigned ax = ABS(x), ay = ABS(y);
+
+ // Check if around one second has passed
+ if ((ticks - data.last_ticks) >= 18) {
+ data.total_motion = 0;
+ data.last_ticks = ticks;
+ }
+
+ // If more than the double speed threshold has been moved in the last second,
+ // double the speed
+ data.total_motion += ax * ax + ay * ay;
+ if (data.total_motion > data.doubleSpeedThreshold * data.doubleSpeedThreshold) {
+ x *= 2;
+ y *= 2;
+ }
+
+ data.delta.x += x;
+ data.delta.y += y;
+
+ data.pos.x += scalei_rem(x, 8, data.mickeysPerLine.x, &data.pos_frac.x);
+ data.pos.y += scalei_rem(y, 8, data.mickeysPerLine.y, &data.pos_frac.y);
+ }
+ bound_position_to_window();
+
+ // TODO: Wheel
+ (void) z;
+
+ // Report movement if there was any
+ if (data.delta.x || data.delta.y) {
+ events |= INT33_EVENT_MASK_MOVEMENT;
+ }
+
+ // Update button status
+ for (i = 0; i < NUM_BUTTONS; ++i) {
+ uint8_t btn = 1 << i;
+ uint8_t evt = 0;
+ if ((buttons & btn) && !(data.buttons & btn)) {
+ // Button pressed
+ evt = 1 << (1 + (i * 2)); // Press event mask
+ data.button[i].pressed.count++;
+ data.button[i].pressed.last.x = data.pos.x;
+ data.button[i].pressed.last.y = data.pos.y;
+ } else if (!(buttons & btn) && (data.buttons & btn)) {
+ // Button released
+ evt = 1 << (2 + (i * 2)); // Release event mask
+ data.button[i].released.count++;
+ data.button[i].released.last.x = data.pos.x;
+ data.button[i].released.last.y = data.pos.y;
+ }
+ if (evt & data.event_mask) {
+ events |= evt;
+ }
+ }
+ data.buttons = buttons;
+
+ refresh_cursor();
+
+ events &= data.event_mask;
+
+ if (data.event_handler && events) {
+ call_event_handler(data.event_handler, events,
+ buttons, data.pos.x, data.pos.y, data.delta.x, data.delta.y);
+
+ // If we succesfully reported movement, clear it to avoid reporting movement again
+ if (events & INT33_EVENT_MASK_MOVEMENT) {
+ //data.delta.x = 0;
+ //data.delta.y = 0;
+ }
+ }
+}
+
+static void __far ps2_mouse_callback(uint8_t status, uint8_t x, uint8_t y, uint8_t z)
+{
+#pragma aux (PS2_CB) ps2_mouse_callback
+
+ int sx = status & PS2M_STATUS_X_NEG ? 0xFF00 | x : x;
+ int sy = -(status & PS2M_STATUS_Y_NEG ? 0xFF00 | y : y);
+ bool abs = false;
+
+#if TOO_VERBOSE
+ dlog_print("ps2 callback status=");
+ dlog_printx(status);
+ dlog_print(" sx=");
+ dlog_printd(sx, 10);
+ dlog_print(" sy=");
+ dlog_printd(sy, 10);
+ dlog_print(" z=");
+ dlog_printd(z, 10);
+ dlog_endline();
+#endif
+
+#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));
+ data.vbhaveabs = true;
+ } else {
+ // VirtualBox does not support absolute coordinates,
+ // or user has disabled them.
+ data.vbhaveabs = false;
+ }
+ }
+#endif
+
+ handle_mouse_event(status & (PS2M_STATUS_BUTTON_1 | PS2M_STATUS_BUTTON_2 | PS2M_STATUS_BUTTON_3),
+ abs, sx, sy, z);
+}
+
+#if USE_VIRTUALBOX
+static void enable_vbox_absolute(bool enable)
+{
+ data.vbhaveabs = false;
+
+ if (data.vbavail) {
+ int err = vbox_set_mouse(&data.vb, enable, false);
+ if (enable && !err) {
+ dlog_puts("VBox absolute mouse enabled");
+ data.vbhaveabs = true;
+ } else if (!enable) {
+ dlog_puts("VBox absolute mouse disabled");
+ }
+ }
+}
+#endif
+
+static void reset_mouse_hardware()
+{
+ ps2m_enable(false);
+
+#if USE_VIRTUALBOX
+ // By default, enable the integration
+ enable_vbox_absolute(true);
+ load_cursor();
+#endif
+
+ ps2m_init(PS2_MOUSE_PLAIN_PACKET_SIZE);
+ ps2m_reset();
+
+ ps2m_set_resolution(3); // 3 = 200 dpi, 8 counts per millimeter
+ ps2m_set_sample_rate(4); // 4 = 80 reports per second
+ ps2m_set_scaling_factor(1); // 1 = 1:1 scaling
+
+ ps2m_set_callback(ps2_mouse_callback);
+
+ ps2m_enable(true);
+}
+
+static void reset_mouse_settings()
+{
+ data.event_mask = 0;
+ data.event_handler = 0;
+
+ data.mickeysPerLine.x = 8;
+ data.mickeysPerLine.y = 16;
+ data.doubleSpeedThreshold = 64;
+ data.min.x = 0;
+ data.max.x = data.screen_max.x;
+ data.min.y = 0;
+ data.max.y = data.screen_max.y;
+ data.visible_count = -1;
+ data.cursor_text_type = 0;
+ data.cursor_text_and_mask = 0xFFFFU;
+ data.cursor_text_xor_mask = 0x7700U;
+ data.cursor_hotspot.x = 0;
+ data.cursor_hotspot.y = 0;
+ nnmemcpy(data.cursor_graphic, default_cursor_graphic, 32+32);
+
+ refresh_cursor(); // This will hide the cursor and update data.cursor_visible
+}
+
+static void reset_mouse_state()
+{
+ int i;
+ data.pos.x = data.min.x;
+ data.pos.y = data.min.y;
+ data.delta.x = 0;
+ data.delta.y = 0;
+ data.delta.x = 0;
+ data.delta.y = 0;
+ data.buttons = 0;
+ for (i = 0; i < NUM_BUTTONS; i++) {
+ data.button[i].pressed.count = 0;
+ data.button[i].pressed.last.x = 0;
+ data.button[i].pressed.last.y = 0;
+ data.button[i].released.count = 0;
+ data.button[i].released.last.x = 0;
+ data.button[i].released.last.y = 0;
+ }
+ data.cursor_visible = false;
+ data.cursor_pos.x = 0;
+ data.cursor_pos.y = 0;
+ data.cursor_prev_char = 0;
+}
+
+static void return_clear_button_counter(union INTPACK __far *r, struct buttoncounter *c)
+{
+ r->x.cx = c->last.x;
+ r->x.dx = c->last.y;
+ r->x.bx = c->count;
+ c->last.x = 0;
+ c->last.y = 0;
+ c->count = 0;
+}
+
+static void int33_handler(union INTPACK r)
+#pragma aux int33_handler "*" parm caller [] modify [ax bx cx dx es]
+{
+ switch (r.x.ax) {
+ case INT33_RESET_MOUSE:
+ dlog_puts("Mouse reset");
+ refresh_video_info();
+ reset_mouse_settings();
+ reset_mouse_hardware();
+ reset_mouse_state();
+ r.x.ax = INT33_MOUSE_FOUND;
+ r.x.bx = NUM_BUTTONS;
+ break;
+ case INT33_SHOW_CURSOR:
+ data.visible_count++;
+ refresh_cursor();
+ break;
+ case INT33_HIDE_CURSOR:
+ data.visible_count--;
+ refresh_cursor();
+ break;
+ case INT33_GET_MOUSE_POSITION:
+ r.x.cx = data.pos.x;
+ r.x.dx = data.pos.y;
+ r.x.bx = data.buttons;
+ break;
+ case INT33_SET_MOUSE_POSITION:
+ data.pos.x = r.x.cx;
+ data.pos.y = r.x.dx;
+ data.delta.x = 0;
+ data.delta.y = 0;
+ bound_position_to_window();
+ break;
+ case INT33_GET_BUTTON_PRESSED_COUNTER:
+ r.x.ax = data.buttons;
+ return_clear_button_counter(&r, &data.button[MIN(r.x.bx, NUM_BUTTONS - 1)].pressed);
+ break;
+ case INT33_GET_BUTTON_RELEASED_COUNTER:
+ r.x.ax = data.buttons;
+ return_clear_button_counter(&r, &data.button[MIN(r.x.bx, NUM_BUTTONS - 1)].released);
+ break;
+ case INT33_SET_HORIZONTAL_WINDOW:
+ dlog_print("Mouse set horizontal window [");
+ dlog_printd(r.x.cx, 10);
+ dlog_putc(',');
+ dlog_printd(r.x.dx, 10);
+ dlog_puts("]");
+ // Recheck in case someone changed the video mode
+ refresh_video_info();
+ data.min.x = r.x.cx;
+ data.max.x = r.x.dx;
+ bound_position_to_window();
+ break;
+ case INT33_SET_VERTICAL_WINDOW:
+ dlog_print("Mouse set vertical window [");
+ dlog_printd(r.x.cx, 10);
+ dlog_putc(',');
+ dlog_printd(r.x.dx, 10);
+ dlog_puts("]");
+ refresh_video_info();
+ data.min.y = r.x.cx;
+ data.max.y = r.x.dx;
+ bound_position_to_window();
+ break;
+ case INT33_SET_GRAPHICS_CURSOR:
+ dlog_puts("Mouse set graphics cursor");
+ data.cursor_hotspot.x = r.x.bx;
+ data.cursor_hotspot.y = r.x.cx;
+ fnmemcpy(data.cursor_graphic, MK_FP(r.x.es, r.x.dx), 64);
+ load_cursor();
+ refresh_cursor();
+ break;
+ case INT33_SET_TEXT_CURSOR:
+ dlog_print("Mouse set text cursor ");
+ dlog_printd(r.x.bx, 10);
+ dlog_endline();
+ data.cursor_text_type = r.x.bx;
+ data.cursor_text_and_mask = r.x.cx;
+ data.cursor_text_xor_mask = r.x.dx;
+ refresh_cursor();
+ break;
+ case INT33_GET_MOUSE_MOTION:
+ r.x.cx = data.delta.x;
+ r.x.dx = data.delta.y;
+ data.delta.x = 0;
+ data.delta.y = 0;
+#if USE_VIRTUALBOX
+ // Likely this means we need a relative mouse, or we will get out of sync
+ //if (data.vbabs) enable_vbox_absolute(false);
+#endif
+ break;
+ case INT33_SET_EVENT_HANDLER:
+ dlog_puts("Mouse set event handler");
+ data.event_mask = r.x.cx;
+ data.event_handler = MK_FP(r.x.es, r.x.dx);
+ break;
+ case INT33_SET_MOUSE_SPEED:
+ dlog_print("Mouse set speed x=");
+ dlog_printd(r.x.cx, 10);
+ dlog_print(" y=");
+ dlog_printd(r.x.dx, 10);
+ dlog_endline();
+ data.mickeysPerLine.x = r.x.cx;
+ data.mickeysPerLine.y = r.x.dx;
+ break;
+ case INT33_SET_SPEED_DOUBLE_THRESHOLD:
+ dlog_print("Mouse set speed double threshold=");
+ dlog_printd(r.x.dx, 10);
+ dlog_endline();
+ data.doubleSpeedThreshold = r.x.dx;
+ break;
+ case INT33_EXCHANGE_EVENT_HANDLER:
+ dlog_puts("Mouse exchange event handler");
+ data.event_mask = r.x.cx;
+ {
+ void (__far *prev_event_handler)() = data.event_handler;
+ data.event_handler = MK_FP(r.x.es, r.x.dx);
+ r.x.es = FP_SEG(prev_event_handler);
+ r.x.dx = FP_OFF(prev_event_handler);
+ }
+ break;
+ case INT33_GET_MOUSE_STATUS_SIZE:
+ dlog_puts("Mouse get status size");
+ r.x.bx = sizeof(TSRDATA);
+ break;
+ case INT33_SAVE_MOUSE_STATUS:
+ dlog_puts("Mouse save status");
+ nfmemcpy(MK_FP(r.x.es, r.x.dx), &data, sizeof(TSRDATA));
+ break;
+ case INT33_LOAD_MOUSE_STATUS:
+ dlog_puts("Mouse load status");
+ fnmemcpy(&data, MK_FP(r.x.es, r.x.dx), sizeof(TSRDATA));
+ break;
+ case INT33_SET_MOUSE_SENSITIVITY:
+ dlog_print("Mouse set speed x=");
+ dlog_printd(r.x.bx, 10);
+ dlog_print(" y=");
+ dlog_printd(r.x.cx, 10);
+ dlog_print(" threshold=");
+ dlog_printd(r.x.dx, 10);
+ dlog_endline();
+ data.mickeysPerLine.x = r.x.bx;
+ data.mickeysPerLine.y = r.x.cx;
+ data.doubleSpeedThreshold = r.x.dx;
+ break;
+ case INT33_GET_MOUSE_SENSITIVITY:
+ r.x.bx = data.mickeysPerLine.x;
+ r.x.cx = data.mickeysPerLine.y;
+ r.x.dx = data.doubleSpeedThreshold;
+ break;
+ case INT33_RESET_SETTINGS:
+ dlog_puts("Mouse reset settings");
+ refresh_video_info();
+ reset_mouse_settings();
+ reset_mouse_state();
+ r.x.ax = INT33_MOUSE_FOUND;
+ r.x.bx = NUM_BUTTONS;
+ break;
+ case INT33_GET_LANGUAGE:
+ r.x.bx = 0;
+ break;
+ case INT33_GET_DRIVER_INFO:
+ dlog_puts("Mouse get driver info");
+ r.h.bh = DRIVER_VERSION_MAJOR;
+ r.h.bl = DRIVER_VERSION_MINOR;
+ r.h.ch = INT33_MOUSE_TYPE_PS2;
+ r.h.cl = 0;
+ break;
+ case INT33_GET_MAX_COORDINATES:
+ r.x.cx = data.screen_max.x;
+ r.x.dx = data.screen_max.y;
+ break;
+ case INT33_GET_WINDOW:
+ r.x.ax = data.min.x;
+ r.x.bx = data.min.y;
+ r.x.cx = data.max.x;
+ r.x.dx = data.max.y;
+ break;
+ case INT33_GET_TSR_DATA:
+ dlog_puts("Get TSR data");
+ r.x.es = FP_SEG(&data);
+ r.x.di = FP_OFF(&data);
+ break;
+ default:
+ dlog_print("Unknown mouse function ax=");
+ dlog_printx(r.x.ax);
+ dlog_endline();
+ break;
+ }
+}
+
+// Can't use __interrupt, it makes a call to GETDS on the runtime
+void __declspec(naked) __far int33_isr(void)
+{
+ __asm {
+ pusha
+ push ds
+ push es
+ push fs
+ push gs
+
+ mov bp, sp
+ push cs
+ pop ds
+
+ call int33_handler
+
+ pop gs
+ pop fs
+ pop es
+ pop ds
+ popa
+ iret
+ }
+}
+
+static LPTSRDATA int33_get_tsr_data(void);
+#pragma aux int33_get_tsr_data = \
+ "xor ax, ax" \
+ "mov es, ax" \
+ "mov di, ax" \
+ "mov ax, 0x7f" \
+ "int 0x33" \
+ __value [es di] \
+ __modify [ax]
+
+static LPTSRDATA local_get_tsr_data(void);
+#pragma aux local_get_tsr_data = \
+ "mov ax, cs" \
+ "mov es, ax" \
+ "mov di, offset data" \
+ __value [es di] \
+ __modify [ax]
+
+LPTSRDATA __far get_tsr_data(bool installed)
+{
+ if (installed) {
+ return int33_get_tsr_data();
+ } else {
+ return local_get_tsr_data();
+ }
+}
+
+int resident_end;
diff --git a/dostsr.h b/dostsr.h
new file mode 100644
index 0000000..c808709
--- /dev/null
+++ b/dostsr.h
@@ -0,0 +1,110 @@
+/*
+ * VBMouse - DOS mouse driver resident part
+ * 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 DOSTSR_H
+#define DOSTSR_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "vbox.h"
+
+#define USE_VIRTUALBOX 1
+
+#define NUM_BUTTONS 3
+
+#define DRIVER_VERSION_MAJOR 8
+#define DRIVER_VERSION_MINOR 0x20
+
+struct point {
+ int16_t x, y;
+};
+
+typedef struct tsrdata {
+ // TSR installation data
+ /** Previous int33 ISR, storing it for uninstall. */
+ void (__interrupt __far *prev_int33_handler)();
+
+ // Video settings
+ uint8_t screen_mode;
+ uint8_t screen_page;
+ bool screen_text_mode;
+ struct point screen_max;
+
+ // Current mouse settings
+ struct point mickeysPerLine; // mickeys per 8 pixels
+ uint16_t doubleSpeedThreshold; // mickeys
+ struct point min;
+ struct point max;
+ int16_t visible_count;
+ uint8_t cursor_text_type;
+ uint16_t cursor_text_and_mask, cursor_text_xor_mask;
+ struct point cursor_hotspot;
+ uint16_t cursor_graphic[16+16];
+
+ // Current mouse status
+ /** Current cursor position (in pixels). */
+ struct point pos;
+ /** Current remainder of movement that does not yet translate to an entire pixel
+ * (8ths of pixel right now). */
+ struct point pos_frac;
+ /** Current delta movement (in mickeys) since the last report. */
+ struct point delta;
+ /** Total mickeys moved in the last second. */
+ uint16_t total_motion;
+ /** Ticks when the above value was last reset. */
+ uint16_t last_ticks;
+ /** Current status of buttons (as bitfield). */
+ uint16_t buttons;
+ struct {
+ struct buttoncounter {
+ struct point last;
+ uint16_t count;
+ } pressed, released;
+ } button[NUM_BUTTONS];
+ /** Whether the cursor is currently displayed or not. */
+ bool cursor_visible;
+ struct point cursor_pos;
+ uint16_t cursor_prev_char;
+
+ // Current handlers
+ void (__far *event_handler)();
+ uint8_t event_mask;
+
+#if USE_VIRTUALBOX
+ /** VirtualBox is available. */
+ bool vbavail : 1;
+ /** Want to use the VirtualBox "host" cursor. */
+ bool vbwantcursor : 1;
+ /** Have VirtualBox absolute coordinates. */
+ bool vbhaveabs : 1;
+ struct vboxcomm vb;
+#endif
+} TSRDATA;
+
+typedef TSRDATA * PTSRDATA;
+typedef TSRDATA __far * LPTSRDATA;
+
+extern void __declspec(naked) __far int33_isr(void);
+
+extern LPTSRDATA __far get_tsr_data(bool installed);
+
+extern int resident_end;
+
+#endif
diff --git a/int10vga.h b/int10vga.h
new file mode 100644
index 0000000..f3e0a07
--- /dev/null
+++ b/int10vga.h
@@ -0,0 +1,41 @@
+#ifndef INT10_H
+#define INT10_H
+
+#include <stdint.h>
+
+#define BIOS_DATA_AREA_SEGMENT 0x40
+
+static uint8_t int10_get_video_mode(uint8_t *screenwidth, uint8_t *videopage);
+#pragma aux int10_get_video_mode = \
+ "mov al, 0" \
+ "mov ah, 0x0F" \
+ "int 0x10" \
+ "mov ss:[si], ah" \
+ "mov ss:[di], bh" \
+ __parm [si] [di] \
+ __value [al] \
+ __modify [ax bh]
+
+static inline uint16_t bda_get_word(unsigned int offset) {
+ uint16_t __far *p = MK_FP(BIOS_DATA_AREA_SEGMENT, offset);
+ return *p;
+}
+
+static inline uint8_t bda_get_byte(unsigned int offset) {
+ uint8_t __far *p = MK_FP(BIOS_DATA_AREA_SEGMENT, offset);
+ return *p;
+}
+
+#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)
+//#define bda_get_tick_count() bda_get_dword(0x6c)
+#define bda_get_tick_count_lo() bda_get_word(0x6c)
+
+static inline uint16_t __far * get_video_char(uint8_t page, unsigned int x, unsigned int y)
+{
+ return MK_FP(0xB800, (page * bda_get_video_page_size())
+ + ((y * bda_get_num_columns()) + x) * 2);
+}
+
+#endif
diff --git a/int33.h b/int33.h
new file mode 100644
index 0000000..c31fc2c
--- /dev/null
+++ b/int33.h
@@ -0,0 +1,197 @@
+/*
+ * VBMouse - int33 API defines
+ * 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 INT33_H
+#define INT33_H
+
+enum INT33_API {
+ /** Reinitializes mouse hardware and resets mouse to default driver values.
+ * On return, ax = 0xFFFF, bx = number of buttons. */
+ INT33_RESET_MOUSE = 0,
+
+ /** Increment the cursor visible counter. The cursor is shown when the counter is >= 0. */
+ INT33_SHOW_CURSOR = 1,
+ /** Decrement the cursor visible counter. The cursor is shown when the counter is >= 0. */
+ INT33_HIDE_CURSOR = 2,
+
+ /** Gets the current mouse position and button status.
+ * @returns cx = horizontal pos, dx = vertical pos,
+ * bx = button status (bitfield, lsb = button 0/left). */
+ INT33_GET_MOUSE_POSITION = 3,
+ /** Sets the current mouse position.
+ * @param cx = horizontal pos, dx = vertical pos. */
+ INT33_SET_MOUSE_POSITION = 4,
+
+ /** Gets the number of times a mouse button was pressed since the last call.
+ * @param bx = button number
+ * @returns ax = current all button status (bitfield),
+ * bx = count of times button was pressed,
+ * cx = horizontal pos at last press,
+ * dx = vetical pos at last press */
+ INT33_GET_BUTTON_PRESSED_COUNTER = 5,
+ /** Gets the number of times a mouse button was released since the last call.
+ * @param bx = button number
+ * @returns ax = current all button status (bitfield),
+ * bx = count of times button was released,
+ * cx = horizontal pos at last release,
+ * dx = vetical pos at last release */
+ INT33_GET_BUTTON_RELEASED_COUNTER = 6,
+
+ /** @param cx = minimum horizontal position, dx = maximum horizontal position. */
+ INT33_SET_HORIZONTAL_WINDOW = 7,
+
+ /** @param cx = minimum vertical position, dx = maximum vertical position. */
+ INT33_SET_VERTICAL_WINDOW = 8,
+
+ /** Configures graphicsmode mouse cursor.
+ * @param bx horizontal hotspot , cx vertical hotspot
+ * @param es:dx address of cursor shape bitmap */
+ INT33_SET_GRAPHICS_CURSOR = 9,
+
+ /** Configures textmode mouse cursor.
+ * @param bx 1 for hardware cursor, 0 for software cursor
+ * @param cx for software cursor, AND mask; for hardware cursor, start scanline.
+ * @param dx for software cursor, XOR mask; for hardware cursor, end scanline. */
+ INT33_SET_TEXT_CURSOR = 0xA,
+
+ /** Gets total relative mouse distance (in mickeys) since the last call.
+ * @param cx horizontal distance , dx vertical distance */
+ INT33_GET_MOUSE_MOTION = 0xB,
+
+ /** Replaces the current routine for event handling.
+ * @param es:dx address of the new event handler
+ * @param cx event mask (see INT33_EVENT_MASK) */
+ INT33_SET_EVENT_HANDLER = 0xC,
+
+ /** Sets the mickey-per-8-pixels ratio, controlling the cursor speed.
+ * @param cx horizontal speed, dx vertical speed */
+ INT33_SET_MOUSE_SPEED = 0xF,
+
+ /** If the mouse is moved more than this mickeys in one second,
+ * the mouse motion is doubled.
+ * @param cx doubling threshold (mickeys per second) */
+ INT33_SET_SPEED_DOUBLE_THRESHOLD = 0x13,
+
+ /** Replaces the current routine for event handling,
+ * but also returns the old one.
+ * @param es:dx address of the new event handler
+ * @param cx event mask (see INT33_EVENT_MASK)
+ * @return es:dx address of the previous event handler. */
+ INT33_EXCHANGE_EVENT_HANDLER = 0x14,
+
+ /** Query the size of the memory required to save a copy of the mouse status.
+ * @return bx buffer needed. */
+ INT33_GET_MOUSE_STATUS_SIZE = 0x15,
+
+ INT33_SAVE_MOUSE_STATUS = 0x16,
+ INT33_LOAD_MOUSE_STATUS = 0x17,
+
+ /** Sets both speed and speed-doubling threshold in one call.
+ * @param bx horizontal speed, cx vertical speed
+ * @param dx doubling threshold (mickeys per second). */
+ INT33_SET_MOUSE_SENSITIVITY = 0x1A,
+ /** Gets current speed and speed-doubling threshold.
+ * @return bx horizontal speed, cx vertical speed
+ * @return dx doubling threshold (mickeys per second). */
+ INT33_GET_MOUSE_SENSITIVITY = 0x1B,
+
+ /** Resets mouse to default driver values.
+ * On return, ax = 0xFFFF, bx = number of buttons. */
+ INT33_RESET_SETTINGS = 0x21,
+
+ /** Gets language for messages.
+ * @return bx language code. */
+ INT33_GET_LANGUAGE = 0x23,
+
+ /** Gets driver information.
+ * On return, bx = major:minor version, ch = INT33_MOUSE_TYPE, CL = irq number (or 0). */
+ INT33_GET_DRIVER_INFO = 0x24,
+
+ /** Gets the current video mode maximum X & Y positions.
+ * @return cx max x, dx max y. */
+ INT33_GET_MAX_COORDINATES = 0x26,
+
+ /** Gets the current window coordinates (set by INT33_SET_HORIZONTAL_WINDOW).
+ * @return ax min_x, bx min_y, cx max_x, d max_y. */
+ INT33_GET_WINDOW = 0x31,
+};
+
+enum INT33_API_INTERNAL {
+ INT33_GET_TSR_DATA = 0x7f
+};
+
+enum {
+ INT33_MOUSE_FOUND = 0xFFFF
+};
+
+enum INT33_MOUSE_TYPE {
+ INT33_MOUSE_TYPE_BUS = 1,
+ INT33_MOUSE_TYPE_SERIAL = 2,
+ INT33_MOUSE_TYPE_INPORT = 3,
+ INT33_MOUSE_TYPE_PS2 = 4,
+ INT33_MOUSE_TYPE_HP = 5
+};
+
+enum INT33_BUTTON_MASK {
+ INT33_BUTTON_MASK_LEFT = 1 << 0,
+ INT33_BUTTON_MASK_RIGHT = 1 << 1,
+ INT33_BUTTON_MASK_CENTER = 1 << 2
+};
+
+enum INT33_EVENT_MASK {
+ INT33_EVENT_MASK_MOVEMENT = 1 << 0,
+ INT33_EVENT_MASK_LEFT_BUTTON_PRESSED = 1 << 1,
+ INT33_EVENT_MASK_LEFT_BUTTON_RELEASED = 1 << 2,
+ INT33_EVENT_MASK_RIGHT_BUTTON_PRESSED = 1 << 3,
+ INT33_EVENT_MASK_RIGHT_BUTTON_RELEASED = 1 << 4,
+ INT33_EVENT_MASK_CENTER_BUTTON_PRESSED = 1 << 5,
+ INT33_EVENT_MASK_CENTER_BUTTON_RELEASED = 1 << 6,
+
+ INT33_EVENT_MASK_ALL = 0xFF
+};
+
+#pragma aux INT33_CB far loadds parm [ax] [bx] [cx] [dx] [si] [di]
+
+static uint16_t int33_reset(void);
+#pragma aux int33_reset = \
+ "mov ax, 0x0" \
+ "int 0x33" \
+ __value [ax]
+
+static void int33_set_horizontal_window(uint16_t min, uint16_t max);
+#pragma aux int33_set_horizontal_window = \
+ "mov ax, 0x7" \
+ "int 0x33" \
+ __parm [cx] [dx] \
+ __modify [ax]
+
+static void int33_set_vertical_window(uint16_t min, uint16_t max);
+#pragma aux int33_set_vertical_window = \
+ "mov ax, 0x8" \
+ "int 0x33" \
+ __parm [cx] [dx] \
+ __modify [ax]
+
+static void int33_set_event_handler(uint16_t event_mask, void (__far *handler)());
+#pragma aux int33_set_event_handler = \
+ "mov ax, 0xC" \
+ "int 0x33" \
+ __parm [cx] [es dx] \
+ __modify [ax]
+
+#endif
diff --git a/makefile b/makefile
index c677d4d..14e60bc 100644
--- a/makefile
+++ b/makefile
@@ -1,26 +1,49 @@
# This is an Open Watcom wmake makefile, not GNU make.
# Assuming you have sourced `owsetenv` beforehand.
+#
+dosobjs = dostsr.obj dosmain.obj vbox.obj
+doscflags = -bt=dos -ms -s -6 -os -w3
+# -ms to use small memory model
+# -s to disable stack checks, since it inserts calls to the runtime from the TSR part
+# -os to optimize for size
+
+w16objs = w16mouse.obj
+w16cflags = -bt=windows -bd -mc -zu -s -6 -w3
+# -bd to build DLL
+# -mc to use compact memory model (far data pointers, since ss != ds)
+# -zu for DLL calling convention (ss != ds)
+# -s to disable stack checks, since the runtime uses MessageBox() to abort (which we can't call from mouse.drv)
.BEFORE:
# We need DOS and Windows headers, not host platform's
set include=$(%watcom)/h/win;$(%watcom)/h
-# The main driver file
-vbmouse.drv: mousew16.c mousew16.h vbox.c vbox.h vboxdev.h ps2.h pci.h vds.h int2fwin.h
- # -bd to build DLL
- # -mc to use compact memory model (far data pointers, since ss != ds)
- # -zu for DLL calling convention (ss != ds)
- # -zc put constants on the code segment (cs)
- # -s to disable stack checks, since the runtime uses MessageBox() to abort (which we can't call from mouse.drv)
- wcl -6 -mc -bd -zu -zc -s -bt=windows -l=windows_dll @vbmouse.lnk -fe=$^@ mousew16.c vbox.c
+# Main DOS driver file
+vbmouse.exe: dosmouse.lnk $(dosobjs)
+ wlink @$[@ name $@ file { $(dosobjs) }
+
+dostsr.obj: dostsr.c .AUTODEPEND
+ wcc -fo=$^@ $(doscflags) -g=RES_GROUP -nd=RES -nt=RES_TEXT -nc=RES_CODE $[@
+
+dosmain.obj: dosmain.c .AUTODEPEND
+ wcc -fo=$^@ $(doscflags) $[@
+
+vbox.obj: vbox.c .AUTODEPEND
+ wcc -fo=$^@ $(doscflags) $[@
+
+vbmouse.drv: w16mouse.lnk $(w16objs)
+ wlink @$[@ name $@ file { $(w16objs) }
+
+w16mouse.obj: w16mouse.c .AUTODEPEND
+ wcc -fo=$^@ $(w16cflags) $[@
clean: .SYMBOLIC
- rm -f vbmouse.drv vbmouse.flp *.o
+ rm -f vbmouse.exe vbmouse.drv vbmouse.flp *.obj *.map
vbmouse.flp:
mformat -C -f 1440 -v VBMOUSE -i $^@ ::
# Build a floppy image containing the driver
-flp: vbmouse.flp vbmouse.drv oemsetup.inf .SYMBOLIC
- mcopy -i vbmouse.flp -o oemsetup.inf vbmouse.drv ::
+flp: vbmouse.flp vbmouse.exe vbmouse.drv .SYMBOLIC
+ mcopy -i vbmouse.flp -o vbmouse.exe vbmouse.drv ::
diff --git a/pci.h b/pci.h
index 265c38a..8d0b7a8 100644
--- a/pci.h
+++ b/pci.h
@@ -50,7 +50,7 @@ static pcierr pci_init_bios(void);
__value [ah] \
__modify [ax bx cx dx]
-static pcierr pci_find_device(pcisel *sel, unsigned short vendor_id, unsigned short dev_id, unsigned short index);
+static pcierr pci_find_device(pcisel __far *sel, unsigned short vendor_id, unsigned short dev_id, unsigned short index);
#pragma aux pci_find_device = \
"mov ax, 0xB102" \
"int 0x1A" \
@@ -63,7 +63,7 @@ static pcierr pci_find_device(pcisel *sel, unsigned short vendor_id, unsigned sh
/* Reading from configuration space */
-static pcierr pci_read_config_byte(pcisel sel, unsigned char reg, unsigned char *data);
+static pcierr pci_read_config_byte(pcisel sel, unsigned char reg, unsigned char __far *data);
#pragma aux pci_read_config_byte = \
"mov ax, 0xB108" \
"int 0x1A" \
@@ -72,7 +72,7 @@ static pcierr pci_read_config_byte(pcisel sel, unsigned char reg, unsigned char
__value [ah] \
__modify [ax cx]
-static pcierr pci_read_config_word(pcisel sel, unsigned char reg, unsigned short *data);
+static pcierr pci_read_config_word(pcisel sel, unsigned char reg, unsigned short __far *data);
#pragma aux pci_read_config_word = \
"mov ax, 0xB109" \
"int 0x1A" \
@@ -81,7 +81,7 @@ static pcierr pci_read_config_word(pcisel sel, unsigned char reg, unsigned short
__value [ah] \
__modify [ax cx]
-static pcierr pci_read_config_dword(pcisel sel, unsigned char reg, unsigned long *data);
+static pcierr pci_read_config_dword(pcisel sel, unsigned char reg, unsigned long __far *data);
#pragma aux pci_read_config_dword = \
"mov ax, 0xB10A" \
"int 0x1A" \
diff --git a/ps2.h b/ps2.h
index 1d0dd37..e5304c4 100644
--- a/ps2.h
+++ b/ps2.h
@@ -68,12 +68,6 @@ enum ps2m_device_ids {
* @param status combination of PS2M_STATUS_* flags */
typedef void (__far * LPFN_PS2CALLBACK)(uint8_t status, uint8_t x, uint8_t y, uint8_t z);
-static inline void cli(void);
-#pragma aux cli = "cli"
-
-static inline void sti(void);
-#pragma aux sti = "sti"
-
static ps2m_err ps2m_init(uint8_t packet_size);
#pragma aux ps2m_init = \
"stc" /* If nothing happens, assume failure */ \
@@ -116,6 +110,10 @@ static ps2m_err ps2m_get_device_id(uint8_t *device_id);
__value [ah] \
__modify [ax]
+// 0 = 25 dpi, 1 count per millimeter
+// 1 = 50 dpi, 2 counts per millimeter
+// 2 = 100 dpi, 4 counts per millimeter
+// 3 = 200 dpi, 8 counts per millimeter
static ps2m_err ps2m_set_resolution(uint8_t resolution);
#pragma aux ps2m_set_resolution = \
"stc" \
@@ -130,6 +128,13 @@ static ps2m_err ps2m_set_resolution(uint8_t resolution);
__value [ah] \
__modify [ax]
+// 0 = 10 reports/sec
+// 1 = 20 reports/sec
+// 2 = 40 reports/sec
+// 3 = 60 reports/sec
+// 4 = 80 reports/sec
+// 5 = 100 reports/sec (default)
+// 6 = 200 reports/sec
static ps2m_err ps2m_set_sample_rate(uint8_t sample_rate);
#pragma aux ps2m_set_sample_rate = \
"stc" \
diff --git a/utils.h b/utils.h
new file mode 100644
index 0000000..4740644
--- /dev/null
+++ b/utils.h
@@ -0,0 +1,94 @@
+#ifndef UTILS_H
+#define UTILS_H
+
+#include <stdint.h>
+
+#define ABS(x) (((x) < 0) ? -(x) : (x))
+
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+
+#define BOUND(x,a,b) ( MIN(b, MAX(a, x)) )
+
+static inline void breakpoint(void);
+#pragma aux breakpoint = 0xcd 0x03;
+
+static inline void cli(void);
+#pragma aux cli = "cli"
+
+static inline void sti(void);
+#pragma aux sti = "sti"
+
+/** Map x linearly from range [0, srcmax] to [0, dstmax].
+ * Equivalent of (x * dstmax) / srcmax but with 32-bit unsigned precision. */
+static unsigned scaleu(unsigned x, unsigned srcmax, unsigned dstmax);
+#pragma aux scaleu = \
+ "mul cx" /* dx:ax = x * dstmax */\
+ "div bx" /* ax = dx:ax / srcmax */\
+ __parm [ax] [bx] [cx] \
+ __value [ax] \
+ __modify [ax cx dx]
+
+/** Map x linearly from range [0, srcmax] to [0, dstmax].
+ * Equivalent of (x * dstmax) / srcmax but with 32-bit signed precision.
+ * Division remainder is returned in rem, which should be reused
+ * in future calls to reduce rounding error. */
+static int scalei_rem(int x, int srcmax, int dstmax, short *rem);
+#pragma aux scalei_rem = \
+ "imul cx" /* dx:ax = x * dstmax */ \
+ "add ax, [si]" /* ax += *rem */ \
+ "adc dx, 0" /* dx += 1 if carry */ \
+ "idiv bx" /* ax = dx:ax / srcmax, dx = new remainder */ \
+ "mov [si], dx" /* store the new remainder */ \
+ __parm [ax] [bx] [cx] [si] \
+ __value [ax] \
+ __modify [ax cx dx]
+
+static void bzero(void __far *buf, unsigned int size);
+#pragma aux bzero = \
+ "cld" \
+ "mov ax, 0" \
+ "rep stos byte ptr es:[di]" \
+ __parm [es di] [cx] \
+ __modify [ax]
+
+static void nfmemcpy(void __far *dst, const void *src, unsigned int size);
+#pragma aux nfmemcpy = \
+ "cld" \
+ "rep movs byte ptr es:[di], byte ptr ds:[si]" \
+ __parm [es di] [si] [cx] \
+ __modify [cx si di]
+
+static void fnmemcpy(void *dst, const void __far *src, unsigned int size);
+#pragma aux fnmemcpy = \
+ "cld" \
+ "push gs" \
+ "mov ax, es" \
+ "mov gs, ax" /* gs is now the src segment */ \
+ "mov ax, ds" \
+ "mov es, ax" /* es is now ds, the dst segment */ \
+ "rep movs byte ptr es:[di], byte ptr gs:[si]" \
+ "pop gs" \
+ __parm [di] [es si] [cx] \
+ __modify [ax cx si di es]
+
+static void ffmemcpy(void __far *dst, const void __far *src, unsigned int size);
+#pragma aux ffmemcpy = \
+ "cld" \
+ "push gs" \
+ "mov gs, ax" \
+ "rep movs byte ptr es:[di], byte ptr gs:[si]" \
+ "pop gs" \
+ __parm [es di] [gs si] [cx] \
+ __modify [cx si di]
+
+static void nnmemcpy(void *dst, const void *src, unsigned int size);
+#pragma aux nnmemcpy = \
+ "cld" \
+ "mov ax, ds" \
+ "mov es, ax" /* es is now ds, the src+dst segment */ \
+ "rep movs byte ptr es:[di], byte ptr ds:[si]" \
+ __parm [di] [si] [cx] \
+ __modify [cx si di es]
+
+#endif
diff --git a/vbmouse.lnk b/vbmouse.lnk
deleted file mode 100644
index 1f260ee..0000000
--- a/vbmouse.lnk
+++ /dev/null
@@ -1,9 +0,0 @@
-OPTION MODNAME=MOUSE # This is necessary; USER.EXE imports mouse functions using this module name
-OPTION DESCRIPTION 'VirtualBox Mouse driver'
-
-SEGMENT CALLBACKS FIXED SHARED # We need a non-moveable segment to store our PS/2 BIOS callbacks
-
-EXPORT Inquire.1
-EXPORT Enable.2
-EXPORT Disable.3
-EXPORT MouseGetIntVect.4
diff --git a/vbox.c b/vbox.c
index b384b6f..6d33659 100644
--- a/vbox.c
+++ b/vbox.c
@@ -17,57 +17,17 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include <windows.h>
#include <string.h>
+#include <stdio.h>
+#include <stdint.h>
#include <i86.h>
#include "pci.h"
#include "vds.h"
+#include "dlog.h"
#include "vboxdev.h"
#include "vbox.h"
-// PCI device information
-/** VBox's PCI bus, device number, etc. */
-static pcisel vbpci;
-/** IO base of VBox's PCI device. */
-static uint16_t vbiobase;
-/** IRQ number of VBox's PCI device. Unused. */
-static uint8_t vbirq;
-
-/** Handle to fixed buffer used for communication with VBox device.
- * We also use Virtual DMA Service to obtain the physical address of this buffer,
- * so it must remain fixed in memory. */
-static HANDLE hBuf;
-static LPVOID pBuf;
-/** The DMA descriptor used by VDS corresponding to the above buffer. */
-static VDS_DDS bufdds;
-
-/** Actually send a request to the VirtualBox VMM device.
- * @param addr 32-bit physical address containing the VMMDevRequest struct.
- */
-static void vbox_send_request(uint32_t addr);
-#pragma aux vbox_send_request = \
- "movzx eax, ax" /* Need to make a single 32-bit write so combine both regs. */ \
- "movzx ebx, bx" \
- "shl ebx, 16" \
- "or eax, ebx" \
- "mov dx, vbiobase" \
- "out dx, eax" \
- "shr ebx, 16" \
- __parm [bx ax] \
- __modify [dx]
-
-/** Acknowledge an interrupt from the VirtualBox VMM device at iobase.
- * @return bitmask with all pending events (e.g. VMMDEV_EVENT_MOUSE_CAPABILITIES_CHANGED). */
-static uint32_t vbox_irq_ack();
-#pragma aux vbox_irq_ack = \
- "mov dx, vbiobase" \
- "add dx, 8" \
- "in eax, dx" \
- "mov edx, eax" \
- "shr edx, 16" \
- __value [dx ax]
-
// Classic PCI defines
#define VBOX_PCI_VEND_ID 0x80ee
#define VBOX_PCI_PROD_ID 0xcafe
@@ -80,60 +40,48 @@ enum {
/** Finds the VirtualBox PCI device and reads the current IO base.
* @returns 0 if the device was found. */
-int vbox_init(void)
+static int vbox_find_iobase(uint16_t __far *iobase)
{
int err;
+ pcisel pcidev;
uint16_t command;
uint32_t bar;
if ((err = pci_init_bios())) {
- return -1;
+ return err;
}
- if ((err = pci_find_device(&vbpci, VBOX_PCI_VEND_ID, VBOX_PCI_PROD_ID, 0))) {
- return -1;
+ if ((err = pci_find_device(&pcidev, VBOX_PCI_VEND_ID, VBOX_PCI_PROD_ID, 0))) {
+ return err;
}
- if ((err = pci_read_config_word(vbpci, CFG_COMMAND, &command))
- || !(command & 1)) {
- return -2;
+ if ((err = pci_read_config_word(pcidev, CFG_COMMAND, &command))) {
+ return err;
}
- if ((err = pci_read_config_byte(vbpci, CFG_INTERRUPT, &vbirq))) {
- return -2;
+ if (!(command & 1)) {
+ // The card is not configured
+ return -1;
}
- if ((err = pci_read_config_dword(vbpci, CFG_BAR0, &bar))) {
- return -2;
+ if ((err = pci_read_config_dword(pcidev, CFG_BAR0, &bar))) {
+ return err;
}
if (!(bar & 1)) {
+ // This is not an IO BAR
return -2;
}
- vbiobase = bar & 0xFFFC;
+ *iobase = bar & 0xFFFC;
return 0;
}
-/** Allocates the buffers that will be used to communicate with the VirtualBox device. */
-int vbox_alloc_buffers(void)
+static int vbox_get_phys_addr(uint32_t __far *physaddr, void __far *buf)
{
- const unsigned int bufferSize = 36; // This should be the largest of all VMMDevRequest* structs that we can send
- int err;
-
- // Allocate the buffer (double the size for reasons explained below)
- hBuf = GlobalAlloc(GMEM_FIXED|GMEM_SHARE, bufferSize * 2);
- if (!hBuf) return -1;
-
- // Keep it in memory at a fixed location
- GlobalFix(hBuf);
- GlobalPageLock(hBuf);
-
- // Get the usable pointer / logical address of the buffer
- pBuf = (LPVOID) GlobalLock(hBuf);
- if (!pBuf) return -1;
-
+ int err = 0;
+#if 0
if (vds_available()) {
// Use the Virtual DMA Service to get the physical address of this buffer
bufdds.regionSize = bufferSize;
@@ -163,152 +111,27 @@ int vbox_alloc_buffers(void)
// If VDS is not available, assume we are not paging
// Just use the linear address as physical
- bufdds.physicalAddress = GetSelectorBase(FP_SEG(pBuf)) + FP_OFF(pBuf);
+ bufdds.physicalAddress = FP_SEG(pBuf + FP_OFF(pBuf);
err = 0;
}
+#endif
- return err;
-}
-
-/** Frees the buffers allocated by vbox_alloc_buffers(). */
-int vbox_free_buffers(void)
-{
- if (bufdds.regionSize > 0) {
- vds_unlock_dma_buffer_region(&bufdds, 0);
- }
- bufdds.regionSize = 0;
- bufdds.segOrSelector = 0;
- bufdds.offset = 0;
- bufdds.bufferId = 0;
- bufdds.physicalAddress = 0;
- GlobalFree(hBuf);
- hBuf = NULL;
- pBuf = NULL;
-
- return 0;
-}
-
-/** Lets VirtualBox know that there are VirtualBox Guest Additions on this guest.
- * @param osType os installed on this guest. */
-int vbox_report_guest_info(uint32_t osType)
-{
- VMMDevReportGuestInfo *req = pBuf;
-
- memset(req, 0, sizeof(VMMDevReportGuestInfo));
-
- req->header.size = sizeof(VMMDevReportGuestInfo);
- req->header.version = VMMDEV_REQUEST_HEADER_VERSION;
- req->header.requestType = VMMDevReq_ReportGuestInfo;
- req->header.rc = -1;
- req->guestInfo.interfaceVersion = VMMDEV_VERSION;
- req->guestInfo.osType = osType;
-
- vbox_send_request(bufdds.physicalAddress);
-
- return req->header.rc;
-}
-
-/** Tells VirtualBox about the events we are interested in receiving.
- These events are notified via the PCI IRQ which we are not using, so this is not used either. */
-int vbox_set_filter_mask(uint32_t add, uint32_t remove)
-{
- VMMDevCtlGuestFilterMask *req = pBuf;
-
- memset(req, 0, sizeof(VMMDevCtlGuestFilterMask));
-
- req->header.size = sizeof(VMMDevCtlGuestFilterMask);
- req->header.version = VMMDEV_REQUEST_HEADER_VERSION;
- req->header.requestType = VMMDevReq_CtlGuestFilterMask;
- req->header.rc = -1;
- req->u32OrMask = add;
- req->u32NotMask = remove;
-
- vbox_send_request(bufdds.physicalAddress);
-
- return req->header.rc;
-}
-
-/** Tells VirtualBox whether we want absolute mouse information or not. */
-int vbox_set_mouse(bool enable)
-{
- VMMDevReqMouseStatus *req = pBuf;
-
- memset(req, 0, sizeof(VMMDevReqMouseStatus));
-
- req->header.size = sizeof(VMMDevReqMouseStatus);
- req->header.version = VMMDEV_REQUEST_HEADER_VERSION;
- req->header.requestType = VMMDevReq_SetMouseStatus;
- req->header.rc = -1;
- req->mouseFeatures = enable ? VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE : 0;
-
- vbox_send_request(bufdds.physicalAddress);
-
- return req->header.rc;
-}
-
-/** Gets the current absolute mouse position from VirtualBox.
- * @param abs false if user has disabled mouse integration in VirtualBox,
- * in which case we should fallback to PS/2 relative events. */
-int vbox_get_mouse(bool *abs, uint16_t *xpos, uint16_t *ypos)
-{
- VMMDevReqMouseStatus *req = pBuf;
-
- memset(req, 0, sizeof(VMMDevReqMouseStatus));
-
- req->header.size = sizeof(VMMDevReqMouseStatus);
- req->header.version = VMMDEV_REQUEST_HEADER_VERSION;
- req->header.requestType = VMMDevReq_GetMouseStatus;
- req->header.rc = -1;
+ *physaddr = ((uint32_t)(FP_SEG(buf)) << 4) + FP_OFF(buf);
- vbox_send_request(bufdds.physicalAddress);
-
- *abs = req->mouseFeatures & VMMDEV_MOUSE_HOST_WANTS_ABSOLUTE;
- *xpos = req->pointerXPos;
- *ypos = req->pointerYPos;
-
- return req->header.rc;
-}
-
-#pragma code_seg ( "CALLBACKS" )
-
-/** This is a version of vbox_set_mouse() that does not call any other functions,
- * and may be called inside an interrupt handler. */
-int vbox_set_mouse_locked(bool enable)
-{
- VMMDevReqMouseStatus *req = pBuf;
-
- req->header.size = sizeof(VMMDevReqMouseStatus);
- req->header.version = VMMDEV_REQUEST_HEADER_VERSION;
- req->header.requestType = VMMDevReq_SetMouseStatus;
- req->header.rc = -1;
- req->mouseFeatures = enable ? VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE : 0;
- req->pointerXPos = 0;
- req->pointerYPos = 0;
-
- vbox_send_request(bufdds.physicalAddress);
-
- return req->header.rc;
+ return err;
}
-/** Likewise for vbox_get_mouse() */
-int vbox_get_mouse_locked(bool *abs, uint16_t *xpos, uint16_t *ypos)
+int vbox_init(LPVBOXCOMM vb)
{
- VMMDevReqMouseStatus *req = pBuf;
-
- req->header.size = sizeof(VMMDevReqMouseStatus);
- req->header.version = VMMDEV_REQUEST_HEADER_VERSION;
- req->header.requestType = VMMDevReq_GetMouseStatus;
- req->header.rc = -1;
- req->mouseFeatures = 0;
- req->pointerXPos = 0;
- req->pointerYPos = 0;
+ int err;
- vbox_send_request(bufdds.physicalAddress);
+ if ((err = vbox_find_iobase(&vb->iobase))) {
+ return err;
+ }
- *abs = req->mouseFeatures & VMMDEV_MOUSE_HOST_WANTS_ABSOLUTE;
- *xpos = req->pointerXPos;
- *ypos = req->pointerYPos;
+ if (err = vbox_get_phys_addr(&vb->buf_physaddr, vb->buf)) {
+ return err;
+ }
- return req->header.rc;
+ return 0;
}
-
diff --git a/vbox.h b/vbox.h
index 2b9c826..486058a 100644
--- a/vbox.h
+++ b/vbox.h
@@ -23,53 +23,164 @@
#include <stdbool.h>
#include <stdint.h>
+#include "utils.h"
+#include "vboxdev.h"
+
+//#define VBOX_BUFFER_SIZE 64
+#define VBOX_BUFFER_SIZE (1024 + 32 + 24 + 20)
+
+typedef struct vboxcomm {
+ uint16_t iobase;
+ char buf[VBOX_BUFFER_SIZE];
+ uint32_t buf_physaddr;
+} vboxcomm_t;
+typedef vboxcomm_t __far * LPVBOXCOMM;
+
/** Logs a single character to the VBox debug message port. */
-static void vbox_logc(char c);
-#pragma aux vbox_logc = \
+static void vbox_log_putc(char c);
+#pragma aux vbox_log_putc = \
"mov dx, 0x504" \
"out dx, al" \
__parm [al] \
__modify [dx]
-/** Logs a string to the VBox debug message port. */
-static void vbox_logs(const char __far *str);
-#pragma aux vbox_logs = \
- "mov di, si" \
- "xor cx, cx" \
- "not cx" /* First we have to strlen(str), prepare -1 as max length */ \
- "xor al, al" /* The '\0' that we're searching for */ \
- "cld" \
- "repne scas byte ptr es:[di]" /* afterwards, cx = -(len+2) */ \
- "not cx" /* cx = (len+1) */ \
- "dec cx" /* cx = len */ \
- "mov dx, 0x504" /* And now we can write the string */ \
- "rep outs dx, byte ptr es:[si]" \
- __parm [es si] \
- __modify [ax cx dx si di]
+/** Actually send a request to the VirtualBox VMM device.
+ * @param addr 32-bit physical address containing the VMMDevRequest struct.
+ */
+static void vbox_send_request(uint16_t iobase, uint32_t addr);
+#pragma aux vbox_send_request = \
+ "push eax" /* Preserve 32-bit register, specifically the higher word. */ \
+ "push bx" /* Combine bx:ax into single 32-bit eax */ \
+ "push ax" \
+ "pop eax" \
+ "out dx, eax" \
+ "pop eax" \
+ __parm [dx] [bx ax] \
+ __modify [dx]
+
+extern int vbox_init(LPVBOXCOMM vb);
+
+/** Lets VirtualBox know that there are VirtualBox Guest Additions on this guest.
+ * @param osType os installed on this guest. */
+static inline int vbox_report_guest_info(LPVBOXCOMM vb, uint32_t osType)
+{
+ VMMDevReportGuestInfo __far *req = (void __far *) vb->buf;
+
+ bzero(req, sizeof(VMMDevReportGuestInfo));
+
+ req->header.size = sizeof(VMMDevReportGuestInfo);
+ req->header.version = VMMDEV_REQUEST_HEADER_VERSION;
+ req->header.requestType = VMMDevReq_ReportGuestInfo;
+ req->header.rc = -1;
+ req->guestInfo.interfaceVersion = VMMDEV_VERSION;
+ req->guestInfo.osType = osType;
+
+ vbox_send_request(vb->iobase, vb->buf_physaddr);
+
+ return req->header.rc;
+}
+
+/** Tells VirtualBox whether we want absolute mouse information or not. */
+static inline int vbox_set_mouse(LPVBOXCOMM vb, bool absolute, bool pointer)
+{
+ VMMDevReqMouseStatus __far *req = (void __far *) vb->buf;
+
+ bzero(req, sizeof(VMMDevReqMouseStatus));
+
+ req->header.size = sizeof(VMMDevReqMouseStatus);
+ req->header.version = VMMDEV_REQUEST_HEADER_VERSION;
+ req->header.requestType = VMMDevReq_SetMouseStatus;
+ req->header.rc = -1;
+ if (absolute) req->mouseFeatures |= VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE;
+ if (pointer) req->mouseFeatures |= VMMDEV_MOUSE_GUEST_NEEDS_HOST_CURSOR;
+
+ vbox_send_request(vb->iobase, vb->buf_physaddr);
+
+ return req->header.rc;
+}
+
+/** Gets the current absolute mouse position from VirtualBox.
+ * @param abs false if user has disabled mouse integration in VirtualBox,
+ * in which case we should fallback to PS/2 relative events. */
+static inline int vbox_get_mouse(LPVBOXCOMM vb, bool *abs,
+ uint16_t *xpos, uint16_t *ypos)
+{
+ VMMDevReqMouseStatus __far *req = (void __far *) vb->buf;
+
+ bzero(req, sizeof(VMMDevReqMouseStatus));
+
+ req->header.size = sizeof(VMMDevReqMouseStatus);
+ req->header.version = VMMDEV_REQUEST_HEADER_VERSION;
+ req->header.requestType = VMMDevReq_GetMouseStatus;
+ req->header.rc = -1;
+
+ vbox_send_request(vb->iobase, vb->buf_physaddr);
+
+ *abs = req->mouseFeatures & VMMDEV_MOUSE_HOST_WANTS_ABSOLUTE;
+ *xpos = req->pointerXPos;
+ *ypos = req->pointerYPos;
+
+ return req->header.rc;
+}
+
+/** @todo */
+static inline int vbox_set_pointer_visible(LPVBOXCOMM vb, bool visible)
+{
+ VMMDevReqMousePointer __far *req = (void __far *) vb->buf;
+
+ bzero(req, sizeof(VMMDevReqMousePointer));
+
+ req->header.size = sizeof(VMMDevReqMousePointer);
+ req->header.version = VMMDEV_REQUEST_HEADER_VERSION;
+ req->header.requestType = VMMDevReq_SetPointerShape;
+ req->header.rc = -1;
+
+ if (visible) req->fFlags |= VBOX_MOUSE_POINTER_VISIBLE;
+
+ vbox_send_request(vb->iobase, vb->buf_physaddr);
-/** Logs a single character followed by '\n' to force log flush.
- * A life-saver when there is no data segment available. */
-#define vbox_putc(c) do { \
- vbox_logc(c); vbox_logc('\n'); \
- } while (0);
+ return req->header.rc;
+}
-extern int vbox_init(void);
+static inline unsigned vbox_pointer_shape_data_size(unsigned width, unsigned height)
+{
+ //unsigned base_size = 24 + 20;
+ unsigned and_mask_size = (width + 7) / 8 * height;
+ unsigned xor_mask_size = width * height * 4;
+ return ((and_mask_size + 3) & ~3) + xor_mask_size;
+}
-extern int vbox_alloc_buffers(void);
-extern int vbox_free_buffers(void);
+static inline int vbox_set_pointer_shape(LPVBOXCOMM vb,
+ uint16_t xHot, uint16_t yHot,
+ uint16_t width, uint16_t height,
+ char __far *data)
+{
+ VMMDevReqMousePointer __far *req = (void __far *) vb->buf;
+ unsigned data_size = vbox_pointer_shape_data_size(width, height);
+ unsigned full_size = MAX(sizeof(VMMDevReqMousePointer), 24 + 20 + data_size);
-extern int vbox_report_guest_info(uint32_t osType);
+ if (full_size >= VBOX_BUFFER_SIZE) {
+ return -2;
+ }
-extern int vbox_set_filter_mask(uint32_t add, uint32_t remove);
+ bzero(req, full_size);
-extern int vbox_set_mouse(bool enable);
+ req->header.size = full_size;
+ req->header.version = VMMDEV_REQUEST_HEADER_VERSION;
+ req->header.requestType = VMMDevReq_SetPointerShape;
+ req->header.rc = -1;
-extern int vbox_get_mouse(bool *abs, uint16_t *xpos, uint16_t *ypos);
+ req->fFlags = VBOX_MOUSE_POINTER_SHAPE;
+ req->xHot = xHot;
+ req->yHot = yHot;
+ req->width = width;
+ req->height = height;
-// In CALLBACKS segment:
+ ffmemcpy(req->pointerData, data, data_size);
-extern int vbox_set_mouse_locked(bool enable);
+ vbox_send_request(vb->iobase, vb->buf_physaddr);
-extern int vbox_get_mouse_locked(bool *abs, uint16_t *xpos, uint16_t *ypos);
+ return req->header.rc;
+}
#endif
diff --git a/vboxdev.h b/vboxdev.h
index 627486f..a9beda1 100644
--- a/vboxdev.h
+++ b/vboxdev.h
@@ -353,6 +353,18 @@ AssertCompileSize(VMMDevReqMouseStatus, 24+12);
#define VMMDEV_MOUSE_RANGE (VMMDEV_MOUSE_RANGE_MAX - VMMDEV_MOUSE_RANGE_MIN)
/** @} */
+/** @name VBVAMOUSEPOINTERSHAPE::fu32Flags
+ * @note The VBOX_MOUSE_POINTER_* flags are used in the guest video driver,
+ * values must be <= 0x8000 and must not be changed. (try make more sense
+ * of this, please).
+ * @{
+ */
+/** pointer is visible */
+#define VBOX_MOUSE_POINTER_VISIBLE (0x0001)
+/** pointer has alpha channel */
+#define VBOX_MOUSE_POINTER_ALPHA (0x0002)
+/** pointerData contains new pointer shape */
+#define VBOX_MOUSE_POINTER_SHAPE (0x0004)
/**
* Mouse pointer shape/visibility change request.
@@ -400,6 +412,7 @@ typedef struct VMMDevReqMousePointer
*/
char pointerData[4];
} VMMDevReqMousePointer;
+AssertCompileSize(VMMDevReqMousePointer, 24+24);
/**
* Pending events structure.
diff --git a/mousew16.c b/w16mouse.c
index 4d54175..93aadf5 100644
--- a/mousew16.c
+++ b/w16mouse.c
@@ -19,20 +19,14 @@
#include <windows.h>
-#include "vbox.h"
-#include "vboxdev.h"
-#include "ps2.h"
+#include "dlog.h"
+#include "utils.h"
+#include "int33.h"
#include "int2fwin.h"
-#include "mousew16.h"
+#include "w16mouse.h"
-/** If 1, actually call VBox services.
- * Thus, if 0, this should behave like a plain PS/2 mouse driver. */
-#define ENABLE_VBOX 1
/** If 1, hook int2f to detect fullscreen DOSBoxes and auto-disable this driver. */
-#define HOOK_INT2F 1
-
-/** Logging through the virtualbox backdoor. */
-#define log(...) vbox_logs(__VA_ARGS__)
+#define HOOK_INT2F 0
#define MOUSE_NUM_BUTTONS 2
@@ -47,10 +41,10 @@ enum {
MOUSEFLAGS_HAS_WIN386 = 1 << 3,
MOUSEFLAGS_INT2F_HOOKED = 1 << 4
};
-/** Last received pressed button status (to compare and see which buttons have been pressed). */
-static unsigned char mousebtnstatus;
+#if HOOK_INT2F
/** Existing interrupt2f handler. */
static LPFN prev_int2f_handler;
+#endif
/* This is how events are delivered to Windows */
@@ -58,58 +52,37 @@ static void send_event(unsigned short Status, short deltaX, short deltaY, short
#pragma aux (MOUSEEVENTPROC) send_event = \
"call dword ptr [eventproc]"
-/* PS/2 BIOS mouse callback. */
-
#pragma code_seg ( "CALLBACKS" )
-static void FAR ps2_mouse_callback(uint8_t status, uint8_t x, uint8_t y, uint8_t z)
-{
-#pragma aux (PS2_CB) ps2_mouse_callback
- int sstatus = 0;
- int sx = status & PS2M_STATUS_X_NEG ? 0xFF00 | x : x;
- int sy = -(status & PS2M_STATUS_Y_NEG ? 0xFF00 | y : y);
+static void FAR int33_mouse_callback(uint16_t events, uint16_t buttons, int16_t x, int16_t y, int16_t delta_x, int16_t delta_y)
+#pragma aux (INT33_CB) int33_mouse_callback
+{
+ int status = 0;
- if (!(mouseflags & MOUSEFLAGS_ENABLED)) {
- // Likely eventproc is invalid
- return;
- }
+ if (events & INT33_EVENT_MASK_LEFT_BUTTON_PRESSED) status |= SF_B1_DOWN;
+ if (events & INT33_EVENT_MASK_LEFT_BUTTON_RELEASED) status |= SF_B1_UP;
+ if (events & INT33_EVENT_MASK_RIGHT_BUTTON_PRESSED) status |= SF_B2_DOWN;
+ if (events & INT33_EVENT_MASK_RIGHT_BUTTON_RELEASED) status |= SF_B2_UP;
- if (sx || sy) {
- sstatus |= SF_MOVEMENT;
+ if (events & INT33_EVENT_MASK_MOVEMENT) {
+ status |= SF_MOVEMENT | SF_ABSOLUTE;
}
-#if ENABLE_VBOX
- if ((sstatus & SF_MOVEMENT) && (mouseflags & MOUSEFLAGS_VBOX_ENABLED)) {
- bool abs;
- uint16_t vbx, vby;
- // Even if we are connected to VBox, the user may have selected to disable abs positioning
- // So only report abs coordinates if it is still enabled.
- if (vbox_get_mouse_locked(&abs, &vbx, &vby) == 0 && abs) {
- sx = vbx;
- sy = vby;
- sstatus |= SF_ABSOLUTE;
- }
- }
+ (void) buttons;
+ (void) delta_x;
+ (void) delta_y;
+
+#if 0
+ dlog_print("w16mouse: event status=");
+ dlog_printx(status);
+ dlog_print(" x=");
+ dlog_printx(x);
+ dlog_print(" y=");
+ dlog_printx(y);
+ dlog_endline();
#endif
- // Now proceed to see which buttons have been pressed down and/or released
- if ((mousebtnstatus & PS2M_STATUS_BUTTON_1) && !(status & PS2M_STATUS_BUTTON_1)) {
- sstatus |= SF_B1_UP;
- } else if (!(mousebtnstatus & PS2M_STATUS_BUTTON_1) && (status & PS2M_STATUS_BUTTON_1)) {
- sstatus |= SF_B1_DOWN;
- }
-
- if ((mousebtnstatus & PS2M_STATUS_BUTTON_2) && !(status & PS2M_STATUS_BUTTON_2)) {
- sstatus |= SF_B2_UP;
- } else if (!(mousebtnstatus & PS2M_STATUS_BUTTON_2) && (status & PS2M_STATUS_BUTTON_2)) {
- sstatus |= SF_B2_DOWN;
- }
-
- mousebtnstatus = status & (PS2M_STATUS_BUTTON_1 | PS2M_STATUS_BUTTON_2);
-
- if (sstatus) {
- send_event(sstatus, sx, sy, MOUSE_NUM_BUTTONS, 0, 0);
- }
+ send_event(status, (uint16_t)(x) * 2, (uint16_t)(y) * 2, MOUSE_NUM_BUTTONS, 0, 0);
}
#if HOOK_INT2F
@@ -123,12 +96,10 @@ static void display_switch_handler(int function)
switch (function) {
case INT2F_NOTIFY_BACKGROUND_SWITCH:
- vbox_logs("Going background\n");
- vbox_set_mouse_locked(false);
+ dlog_puts("Going background\n");
break;
case INT2F_NOTIFY_FOREGROUND_SWITCH:
- vbox_logs("Going foreground\n");
- vbox_set_mouse_locked(true);
+ dlog_puts("Going foreground\n");
break;
}
}
@@ -202,22 +173,12 @@ BOOL FAR PASCAL LibMain(HINSTANCE hInstance, WORD wDataSegment,
}
#endif
-#if ENABLE_VBOX
- // However we will check whether VirtualBox exists:
- if (vbox_init() == 0) {
- vbox_logs("VirtualBox found\n");
-
- // VirtualBox connection was succesful, remember that
- mouseflags |= MOUSEFLAGS_HAS_VBOX;
- }
-#endif
-
// When running under protected mode Windows, let's tell VMD (the mouse virtualizer)
// what type of mouse we are going to be using
if (mouseflags & MOUSEFLAGS_HAS_WIN386) {
LPFN vmd_entry = win_get_vxd_api_entry(VMD_DEVICE_ID);
if (vmd_entry) {
- vmd_set_mouse_type(&vmd_entry, VMD_TYPE_PS2, PS2_MOUSE_INT_VECTOR, 0);
+ vmd_set_mouse_type(&vmd_entry, VMD_TYPE_PS2, 0x33, 0);
}
}
@@ -228,9 +189,9 @@ BOOL FAR PASCAL LibMain(HINSTANCE hInstance, WORD wDataSegment,
WORD FAR PASCAL Inquire(LPMOUSEINFO lpMouseInfo)
{
lpMouseInfo->msExist = 1;
- lpMouseInfo->msRelative = mouseflags & MOUSEFLAGS_HAS_VBOX ? 0 : 1;
+ lpMouseInfo->msRelative = 0;
lpMouseInfo->msNumButtons = MOUSE_NUM_BUTTONS;
- lpMouseInfo->msRate = 40;
+ lpMouseInfo->msRate = 80;
return sizeof(MOUSEINFO);
}
@@ -244,59 +205,25 @@ VOID FAR PASCAL Enable(LPFN_MOUSEEVENT lpEventProc)
sti();
if (!(mouseflags & MOUSEFLAGS_ENABLED)) {
- // Configure the PS/2 bios and reset the mouse
- int err;
- if ((err = ps2m_init(PS2_MOUSE_PLAIN_PACKET_SIZE))) {
- vbox_logs("PS2 init failure\n");
- return;
- }
+ dlog_puts("w16mouse: enable");
- // Configure mouse settings; just use the same defaults as Windows.
- // Note that protected mode Windows just ignores all of these anyway.
- ps2m_set_resolution(3); // 3 = 200 dpi, 8 counts per millimeter
- ps2m_set_sample_rate(2); // 2 = 40 reports per second
- ps2m_set_scaling_factor(1); // 1 = 1:1 scaling
- // Don't check errors for these, we don't care much
+ int33_reset();
- if ((err = ps2m_set_callback(ps2_mouse_callback))) {
- vbox_logs("PS2 set handler failure\n");
- return;
- }
-
- if ((err = ps2m_enable(true))) {
- vbox_logs("PS2 enable failure\n");
- return;
- }
-
- vbox_logs("PS/2 Enabled!\n");
- mouseflags |= MOUSEFLAGS_ENABLED;
+ int33_set_horizontal_window(0, 0x7FFF);
+ int33_set_vertical_window(0, 0x7FFF);
-#if ENABLE_VBOX
- if (mouseflags & MOUSEFLAGS_HAS_VBOX) {
- if ((err = vbox_alloc_buffers())) {
- vbox_logs("VBox alloc failure\n");
- return;
- }
+ int33_set_event_handler(INT33_EVENT_MASK_ALL, int33_mouse_callback);
- vbox_report_guest_info(VBOXOSTYPE_Win31);
+ dlog_puts("w16mouse: int33 enabled");
- if ((err = vbox_set_mouse(true))) {
- vbox_logs("VBox enable failure\n");
- vbox_free_buffers();
- return;
- }
-
- vbox_logs("VBOX Enabled!\n");
- mouseflags |= MOUSEFLAGS_VBOX_ENABLED;
- }
-#endif
+ mouseflags |= MOUSEFLAGS_ENABLED;
#if HOOK_INT2F
if ((mouseflags & MOUSEFLAGS_HAS_WIN386) && (mouseflags & MOUSEFLAGS_VBOX_ENABLED)) {
cli();
hook_int2f(&prev_int2f_handler, int2f_handler);
sti();
- vbox_logs("int2F hooked!\n");
+ dlog_puts("int2F hooked!\n");
mouseflags |= MOUSEFLAGS_INT2F_HOOKED;
}
#endif
@@ -307,36 +234,28 @@ VOID FAR PASCAL Enable(LPFN_MOUSEEVENT lpEventProc)
VOID FAR PASCAL Disable(VOID)
{
if (mouseflags & MOUSEFLAGS_ENABLED) {
+ dlog_puts("w16mouse: disable");
+
#if HOOK_INT2F
if (mouseflags & MOUSEFLAGS_INT2F_HOOKED) {
cli();
unhook_int2f(prev_int2f_handler);
sti();
- vbox_logs("int2F unhooked!\n");
+ dlog_puts("int2F unhooked!\n");
mouseflags &= ~MOUSEFLAGS_INT2F_HOOKED;
}
#endif
- ps2m_enable(false);
- ps2m_set_callback(NULL);
- vbox_logs("PS2 Disabled!\n");
+ int33_reset();
+ dlog_puts("w16mouse: int33 reset");
mouseflags &= ~MOUSEFLAGS_ENABLED;
-
-#if ENABLE_VBOX
- if (mouseflags & MOUSEFLAGS_VBOX_ENABLED) {
- vbox_set_mouse(false);
- vbox_free_buffers();
- vbox_logs("VBOX Disabled!\n");
- mouseflags &= ~MOUSEFLAGS_VBOX_ENABLED;
- }
-#endif
}
}
/** Called by Window to retrieve the interrupt vector number used by this driver, or -1. */
int FAR PASCAL MouseGetIntVect(VOID)
{
- return PS2_MOUSE_INT_VECTOR;
+ return 0x33;
}
diff --git a/mousew16.h b/w16mouse.h
index 54b262f..54b262f 100644
--- a/mousew16.h
+++ b/w16mouse.h
diff --git a/w16mouse.lnk b/w16mouse.lnk
new file mode 100644
index 0000000..bb80eb2
--- /dev/null
+++ b/w16mouse.lnk
@@ -0,0 +1,11 @@
+system windows_dll
+option map=w16mouse.map
+option modname=MOUSE # This is necessary; USER.EXE imports mouse functions using this module name
+option description 'VirtualBox Mouse driver'
+
+segment CALLBACKS fixed shared # We need a non-moveable segment to store our callback routines
+
+export Inquire.1
+export Enable.2
+export Disable.3
+export MouseGetIntVect.4