diff options
-rw-r--r-- | .gitignore | 4 | ||||
-rw-r--r-- | dlog.h | 87 | ||||
-rw-r--r-- | dosmain.c | 275 | ||||
-rw-r--r-- | dosmouse.lnk | 9 | ||||
-rw-r--r-- | dostsr.c | 740 | ||||
-rw-r--r-- | dostsr.h | 110 | ||||
-rw-r--r-- | int10vga.h | 41 | ||||
-rw-r--r-- | int33.h | 197 | ||||
-rw-r--r-- | makefile | 45 | ||||
-rw-r--r-- | pci.h | 8 | ||||
-rw-r--r-- | ps2.h | 17 | ||||
-rw-r--r-- | utils.h | 94 | ||||
-rw-r--r-- | vbmouse.lnk | 9 | ||||
-rw-r--r-- | vbox.c | 243 | ||||
-rw-r--r-- | vbox.h | 175 | ||||
-rw-r--r-- | vboxdev.h | 13 | ||||
-rw-r--r-- | w16mouse.c (renamed from mousew16.c) | 179 | ||||
-rw-r--r-- | w16mouse.h (renamed from mousew16.h) | 0 | ||||
-rw-r--r-- | w16mouse.lnk | 11 |
19 files changed, 1854 insertions, 403 deletions
@@ -1,5 +1,7 @@ vbmouse.drv -vbmouse.map +vbmouse.exe vbmouse.flp *.o +*.obj +*.map runvm.sh @@ -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 @@ -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 @@ -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 :: @@ -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" \ @@ -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" \ @@ -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 @@ -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; } - @@ -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 @@ -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. @@ -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/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 |