From a816d1a09b1045fb5c155ac73f3231fcf9d93180 Mon Sep 17 00:00:00 2001 From: Javier Date: Tue, 29 Mar 2022 01:15:53 +0200 Subject: initial import of DOS mouse driver --- .gitignore | 4 +- dlog.h | 87 +++++++ dosmain.c | 275 ++++++++++++++++++++++ dosmouse.lnk | 9 + dostsr.c | 740 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ dostsr.h | 110 +++++++++ int10vga.h | 41 ++++ int33.h | 197 ++++++++++++++++ makefile | 45 +++- mousew16.c | 342 --------------------------- mousew16.h | 53 ----- pci.h | 8 +- ps2.h | 17 +- utils.h | 94 ++++++++ vbmouse.lnk | 9 - vbox.c | 243 +++----------------- vbox.h | 175 +++++++++++--- vboxdev.h | 13 ++ w16mouse.c | 261 +++++++++++++++++++++ w16mouse.h | 53 +++++ w16mouse.lnk | 11 + 21 files changed, 2119 insertions(+), 668 deletions(-) create mode 100644 dlog.h create mode 100644 dosmain.c create mode 100644 dosmouse.lnk create mode 100644 dostsr.c create mode 100644 dostsr.h create mode 100644 int10vga.h create mode 100644 int33.h delete mode 100644 mousew16.c delete mode 100644 mousew16.h create mode 100644 utils.h delete mode 100644 vbmouse.lnk create mode 100644 w16mouse.c create mode 100644 w16mouse.h create mode 100644 w16mouse.lnk 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 +#include +#include +#include +#include + +#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 \n\n" + "Supported actions:\n" + "\tinstall install the driver (default)\n" + "\tuninstall uninstall the driver from memory\n" + "\tinteg enable/disable virtualbox integration\n" + "\thostcur 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 + +#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 +#include + +#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 + +#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/mousew16.c b/mousew16.c deleted file mode 100644 index 4d54175..0000000 --- a/mousew16.c +++ /dev/null @@ -1,342 +0,0 @@ -/* - * VBMouse - win16 mouse driver entry points - * 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 - -#include "vbox.h" -#include "vboxdev.h" -#include "ps2.h" -#include "int2fwin.h" -#include "mousew16.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 MOUSE_NUM_BUTTONS 2 - -/** The routine Windows gave us which we should use to report events. */ -static LPFN_MOUSEEVENT eventproc; -/** Current status of the mouse driver (see MOUSEFLAGS_*). */ -static unsigned char mouseflags; -enum { - MOUSEFLAGS_ENABLED = 1 << 0, - MOUSEFLAGS_HAS_VBOX = 1 << 1, - MOUSEFLAGS_VBOX_ENABLED = 1 << 2, - 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; -/** Existing interrupt2f handler. */ -static LPFN prev_int2f_handler; - -/* This is how events are delivered to Windows */ - -static void send_event(unsigned short Status, short deltaX, short deltaY, short ButtonCount, short extra1, short extra2); -#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); - - if (!(mouseflags & MOUSEFLAGS_ENABLED)) { - // Likely eventproc is invalid - return; - } - - if (sx || sy) { - sstatus |= SF_MOVEMENT; - } - -#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; - } - } -#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); - } -} - -#if HOOK_INT2F - -static void display_switch_handler(int function) -#pragma aux display_switch_handler parm caller [ax] modify [ax bx cx dx si di] -{ - if (!(mouseflags & MOUSEFLAGS_ENABLED) || !(mouseflags & MOUSEFLAGS_VBOX_ENABLED)) { - return; - } - - switch (function) { - case INT2F_NOTIFY_BACKGROUND_SWITCH: - vbox_logs("Going background\n"); - vbox_set_mouse_locked(false); - break; - case INT2F_NOTIFY_FOREGROUND_SWITCH: - vbox_logs("Going foreground\n"); - vbox_set_mouse_locked(true); - break; - } -} - -/** Interrupt 2F handler, which will be called on some Windows 386 mode events. - * This is more complicated than it should be becaused we have to fetch our DS - * without clobbering any registers whatsoever, and chain back to the previous handler. - * @todo OpenWatcom 2.x insists on calling GETDS from a different code segment, so we can't use __interrupt. */ -static void __declspec(naked) __far int2f_handler(void) -{ - _asm { - ; Preserve data segment - push ds - - ; Load our data segment - push ax - mov ax, SEG prev_int2f_handler ; Let's hope that Windows relocates segments with interrupts disabled - mov ds, ax - pop ax - - ; Check functions we are interested in hooking - cmp ax, 0x4001 ; Notify Background Switch - je handle_it - cmp ax, 0x4002 ; Notify Foreground Switch - je handle_it - - ; Otherwise directly jump to next handler - jmp next_handler - - handle_it: - pushad ; Save and restore 32-bit registers, we may clobber them from C - call display_switch_handler - popad - - next_handler: - ; Store the address of the previous handler - push dword ptr [prev_int2f_handler] - - ; Restore original data segment without touching the stack, - ; since we want to keep the prev handler address at the top - push bp - mov bp, sp - mov ds, [bp + 6] ; Stack looks like 0: bp, 2: prev_int2f_handler, 6: ds - pop bp - - retf 2 - } -} - -#endif /* HOOK_INT2F */ - -#pragma code_seg () - -/* Driver exported functions. */ - -/** DLL entry point (or driver initialization routine). - * The initialization routine should check whether a mouse exists. - * @return nonzero value indicates a mouse exists. - */ -#pragma off (unreferenced); -BOOL FAR PASCAL LibMain(HINSTANCE hInstance, WORD wDataSegment, - WORD wHeapSize, LPSTR lpszCmdLine) -#pragma pop (unreferenced); -{ - // We are not going to bother checking whether a PS2 mouse exists and just assume it does - -#if HOOK_INT2F - // Check now whether we are running under protected mode windows - if (windows_386_enhanced_mode()) { - mouseflags |= MOUSEFLAGS_HAS_WIN386; - } -#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); - } - } - - return 1; -} - -/** Called by Windows to retrieve information about the mouse hardware. */ -WORD FAR PASCAL Inquire(LPMOUSEINFO lpMouseInfo) -{ - lpMouseInfo->msExist = 1; - lpMouseInfo->msRelative = mouseflags & MOUSEFLAGS_HAS_VBOX ? 0 : 1; - lpMouseInfo->msNumButtons = MOUSE_NUM_BUTTONS; - lpMouseInfo->msRate = 40; - return sizeof(MOUSEINFO); -} - -/** Called by Windows to enable the mouse driver. - * @param lpEventProc Callback function to call when a mouse event happens. */ -VOID FAR PASCAL Enable(LPFN_MOUSEEVENT lpEventProc) -{ - // Store the windows-given callback - cli(); // Write to far pointer may not be atomic, and we could be interrupted mid-write - eventproc = 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; - } - - // 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 - - 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; - -#if ENABLE_VBOX - if (mouseflags & MOUSEFLAGS_HAS_VBOX) { - if ((err = vbox_alloc_buffers())) { - vbox_logs("VBox alloc failure\n"); - return; - } - - vbox_report_guest_info(VBOXOSTYPE_Win31); - - 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 - -#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"); - mouseflags |= MOUSEFLAGS_INT2F_HOOKED; - } -#endif - } -} - -/** Called by Windows to disable the mouse driver. */ -VOID FAR PASCAL Disable(VOID) -{ - if (mouseflags & MOUSEFLAGS_ENABLED) { -#if HOOK_INT2F - if (mouseflags & MOUSEFLAGS_INT2F_HOOKED) { - cli(); - unhook_int2f(prev_int2f_handler); - sti(); - vbox_logs("int2F unhooked!\n"); - mouseflags &= ~MOUSEFLAGS_INT2F_HOOKED; - } -#endif - - ps2m_enable(false); - ps2m_set_callback(NULL); - vbox_logs("PS2 Disabled!\n"); - - 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; -} - diff --git a/mousew16.h b/mousew16.h deleted file mode 100644 index 54b262f..0000000 --- a/mousew16.h +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef MOUSEW16_H -#define MOUSEW16_H - -/* Win16's mouse driver interface. */ - -/** Contains information about the mouse, used by Inquire(). */ -typedef _Packed struct MOUSEINFO -{ - /** Whether a mouse exists. */ - char msExist; - /** Whether the mouse returns absolute or relative coordinates. */ - char msRelative; - /** Number of buttons. */ - short msNumButtons; - /** Maximum number of events per second. */ - short msRate; - // Reserved: - short msXThreshold; - short msYThreshold; - short msXRes; - short msYRes; - // The following are available in Windows >= 3.1 only: - #if 0 - /** Specifies the COM port used, or 0 for none. */ - short msMouseCommPort; - #endif -} MOUSEINFO; -typedef MOUSEINFO __far *LPMOUSEINFO; - -/** Movement occurred. */ -#define SF_MOVEMENT 0x0001 -/** Button 1 changed to down. */ -#define SF_B1_DOWN 0x0002 -/** Button 1 changed to up. */ -#define SF_B1_UP 0x0004 -/** Button 2 changed to down. */ -#define SF_B2_DOWN 0x0008 -/** Button 2 changed to up. */ -#define SF_B2_UP 0x0010 -/** Event coordinates are absolute instead of relative. */ -#define SF_ABSOLUTE 0x8000 - -/** Driver should call this callback when there are new mouse events to report. - * @param Status What happened. Combination of SF_MOVEMENT, SF_ABSOLUTE, etc. - * @param deltaX either number of mickeys moved or absolute coordinate if SB_ABSOLUTE. - * @param deltaY either number of mickeys moved or absolute coordinate if SB_ABSOLUTE. - * @param ButtonCount number of buttons - * @param extra1,extra2 leave as zero - */ -typedef void (__far *LPFN_MOUSEEVENT)(unsigned short Status, short deltaX, short deltaY, short ButtonCount, short extra1, short extra2); -#pragma aux MOUSEEVENTPROC parm [ax] [bx] [cx] [dx] [di] [si] - -#endif 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 + +#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 #include +#include +#include #include #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 #include +#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/w16mouse.c b/w16mouse.c new file mode 100644 index 0000000..93aadf5 --- /dev/null +++ b/w16mouse.c @@ -0,0 +1,261 @@ +/* + * VBMouse - win16 mouse driver entry points + * 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 + +#include "dlog.h" +#include "utils.h" +#include "int33.h" +#include "int2fwin.h" +#include "w16mouse.h" + +/** If 1, hook int2f to detect fullscreen DOSBoxes and auto-disable this driver. */ +#define HOOK_INT2F 0 + +#define MOUSE_NUM_BUTTONS 2 + +/** The routine Windows gave us which we should use to report events. */ +static LPFN_MOUSEEVENT eventproc; +/** Current status of the mouse driver (see MOUSEFLAGS_*). */ +static unsigned char mouseflags; +enum { + MOUSEFLAGS_ENABLED = 1 << 0, + MOUSEFLAGS_HAS_VBOX = 1 << 1, + MOUSEFLAGS_VBOX_ENABLED = 1 << 2, + MOUSEFLAGS_HAS_WIN386 = 1 << 3, + MOUSEFLAGS_INT2F_HOOKED = 1 << 4 +}; +#if HOOK_INT2F +/** Existing interrupt2f handler. */ +static LPFN prev_int2f_handler; +#endif + +/* This is how events are delivered to Windows */ + +static void send_event(unsigned short Status, short deltaX, short deltaY, short ButtonCount, short extra1, short extra2); +#pragma aux (MOUSEEVENTPROC) send_event = \ + "call dword ptr [eventproc]" + +#pragma code_seg ( "CALLBACKS" ) + +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 (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 (events & INT33_EVENT_MASK_MOVEMENT) { + status |= SF_MOVEMENT | 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 + + send_event(status, (uint16_t)(x) * 2, (uint16_t)(y) * 2, MOUSE_NUM_BUTTONS, 0, 0); +} + +#if HOOK_INT2F + +static void display_switch_handler(int function) +#pragma aux display_switch_handler parm caller [ax] modify [ax bx cx dx si di] +{ + if (!(mouseflags & MOUSEFLAGS_ENABLED) || !(mouseflags & MOUSEFLAGS_VBOX_ENABLED)) { + return; + } + + switch (function) { + case INT2F_NOTIFY_BACKGROUND_SWITCH: + dlog_puts("Going background\n"); + break; + case INT2F_NOTIFY_FOREGROUND_SWITCH: + dlog_puts("Going foreground\n"); + break; + } +} + +/** Interrupt 2F handler, which will be called on some Windows 386 mode events. + * This is more complicated than it should be becaused we have to fetch our DS + * without clobbering any registers whatsoever, and chain back to the previous handler. + * @todo OpenWatcom 2.x insists on calling GETDS from a different code segment, so we can't use __interrupt. */ +static void __declspec(naked) __far int2f_handler(void) +{ + _asm { + ; Preserve data segment + push ds + + ; Load our data segment + push ax + mov ax, SEG prev_int2f_handler ; Let's hope that Windows relocates segments with interrupts disabled + mov ds, ax + pop ax + + ; Check functions we are interested in hooking + cmp ax, 0x4001 ; Notify Background Switch + je handle_it + cmp ax, 0x4002 ; Notify Foreground Switch + je handle_it + + ; Otherwise directly jump to next handler + jmp next_handler + + handle_it: + pushad ; Save and restore 32-bit registers, we may clobber them from C + call display_switch_handler + popad + + next_handler: + ; Store the address of the previous handler + push dword ptr [prev_int2f_handler] + + ; Restore original data segment without touching the stack, + ; since we want to keep the prev handler address at the top + push bp + mov bp, sp + mov ds, [bp + 6] ; Stack looks like 0: bp, 2: prev_int2f_handler, 6: ds + pop bp + + retf 2 + } +} + +#endif /* HOOK_INT2F */ + +#pragma code_seg () + +/* Driver exported functions. */ + +/** DLL entry point (or driver initialization routine). + * The initialization routine should check whether a mouse exists. + * @return nonzero value indicates a mouse exists. + */ +#pragma off (unreferenced); +BOOL FAR PASCAL LibMain(HINSTANCE hInstance, WORD wDataSegment, + WORD wHeapSize, LPSTR lpszCmdLine) +#pragma pop (unreferenced); +{ + // We are not going to bother checking whether a PS2 mouse exists and just assume it does + +#if HOOK_INT2F + // Check now whether we are running under protected mode windows + if (windows_386_enhanced_mode()) { + mouseflags |= MOUSEFLAGS_HAS_WIN386; + } +#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, 0x33, 0); + } + } + + return 1; +} + +/** Called by Windows to retrieve information about the mouse hardware. */ +WORD FAR PASCAL Inquire(LPMOUSEINFO lpMouseInfo) +{ + lpMouseInfo->msExist = 1; + lpMouseInfo->msRelative = 0; + lpMouseInfo->msNumButtons = MOUSE_NUM_BUTTONS; + lpMouseInfo->msRate = 80; + return sizeof(MOUSEINFO); +} + +/** Called by Windows to enable the mouse driver. + * @param lpEventProc Callback function to call when a mouse event happens. */ +VOID FAR PASCAL Enable(LPFN_MOUSEEVENT lpEventProc) +{ + // Store the windows-given callback + cli(); // Write to far pointer may not be atomic, and we could be interrupted mid-write + eventproc = lpEventProc; + sti(); + + if (!(mouseflags & MOUSEFLAGS_ENABLED)) { + dlog_puts("w16mouse: enable"); + + int33_reset(); + + int33_set_horizontal_window(0, 0x7FFF); + int33_set_vertical_window(0, 0x7FFF); + + int33_set_event_handler(INT33_EVENT_MASK_ALL, int33_mouse_callback); + + dlog_puts("w16mouse: int33 enabled"); + + mouseflags |= MOUSEFLAGS_ENABLED; + +#if HOOK_INT2F + if ((mouseflags & MOUSEFLAGS_HAS_WIN386) && (mouseflags & MOUSEFLAGS_VBOX_ENABLED)) { + cli(); + hook_int2f(&prev_int2f_handler, int2f_handler); + sti(); + dlog_puts("int2F hooked!\n"); + mouseflags |= MOUSEFLAGS_INT2F_HOOKED; + } +#endif + } +} + +/** Called by Windows to disable the mouse driver. */ +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(); + dlog_puts("int2F unhooked!\n"); + mouseflags &= ~MOUSEFLAGS_INT2F_HOOKED; + } +#endif + + int33_reset(); + dlog_puts("w16mouse: int33 reset"); + + mouseflags &= ~MOUSEFLAGS_ENABLED; + } +} + +/** Called by Window to retrieve the interrupt vector number used by this driver, or -1. */ +int FAR PASCAL MouseGetIntVect(VOID) +{ + return 0x33; +} + diff --git a/w16mouse.h b/w16mouse.h new file mode 100644 index 0000000..54b262f --- /dev/null +++ b/w16mouse.h @@ -0,0 +1,53 @@ +#ifndef MOUSEW16_H +#define MOUSEW16_H + +/* Win16's mouse driver interface. */ + +/** Contains information about the mouse, used by Inquire(). */ +typedef _Packed struct MOUSEINFO +{ + /** Whether a mouse exists. */ + char msExist; + /** Whether the mouse returns absolute or relative coordinates. */ + char msRelative; + /** Number of buttons. */ + short msNumButtons; + /** Maximum number of events per second. */ + short msRate; + // Reserved: + short msXThreshold; + short msYThreshold; + short msXRes; + short msYRes; + // The following are available in Windows >= 3.1 only: + #if 0 + /** Specifies the COM port used, or 0 for none. */ + short msMouseCommPort; + #endif +} MOUSEINFO; +typedef MOUSEINFO __far *LPMOUSEINFO; + +/** Movement occurred. */ +#define SF_MOVEMENT 0x0001 +/** Button 1 changed to down. */ +#define SF_B1_DOWN 0x0002 +/** Button 1 changed to up. */ +#define SF_B1_UP 0x0004 +/** Button 2 changed to down. */ +#define SF_B2_DOWN 0x0008 +/** Button 2 changed to up. */ +#define SF_B2_UP 0x0010 +/** Event coordinates are absolute instead of relative. */ +#define SF_ABSOLUTE 0x8000 + +/** Driver should call this callback when there are new mouse events to report. + * @param Status What happened. Combination of SF_MOVEMENT, SF_ABSOLUTE, etc. + * @param deltaX either number of mickeys moved or absolute coordinate if SB_ABSOLUTE. + * @param deltaY either number of mickeys moved or absolute coordinate if SB_ABSOLUTE. + * @param ButtonCount number of buttons + * @param extra1,extra2 leave as zero + */ +typedef void (__far *LPFN_MOUSEEVENT)(unsigned short Status, short deltaX, short deltaY, short ButtonCount, short extra1, short extra2); +#pragma aux MOUSEEVENTPROC parm [ax] [bx] [cx] [dx] [di] [si] + +#endif 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 -- cgit v1.2.3