From 3e39df4a4185f947d1af564aca265c0f6b51c9ec Mon Sep 17 00:00:00 2001 From: Javier Date: Sat, 2 Apr 2022 01:14:57 +0200 Subject: implement graphic cursor for CGA modes, wheel mouse detection, int2f hooking, simplify w16 driver --- dlog.h | 82 +++++++--- dosmain.c | 66 +++++++- dostsr.c | 526 +++++++++++++++++++++++++++++++++++++++++++++++-------------- dostsr.h | 56 ++++++- int10vga.h | 31 +++- int2fwin.h | 17 ++ int33.h | 10 +- makefile | 14 +- ps2.h | 73 +++++++-- utils.h | 21 ++- vbox.h | 51 +----- w16mouse.c | 99 +++++++----- 12 files changed, 784 insertions(+), 262 deletions(-) diff --git a/dlog.h b/dlog.h index dfa346d..67f430a 100644 --- a/dlog.h +++ b/dlog.h @@ -1,3 +1,22 @@ +/* + * VBMouse - printf & logging routines + * Copyright (C) 2022 Javier S. Pedro + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + #ifndef DLOG_H #define DLOG_H @@ -10,12 +29,10 @@ #define dlog_putc vbox_log_putc #endif -static inline void dlog_endline(void) -{ - dlog_putc('\n'); -} +#define dlog_endline() dlog_putc('\n') -static inline void dlog_print(const char *s) +/** Print string to log */ +static void dlog_print(const char *s) { char c; while (c = *s++) { @@ -23,9 +40,17 @@ static inline void dlog_print(const char *s) } } -static inline void dlog_printu(unsigned int num, int base) +/** Print + newline */ +static void dlog_puts(const char *s) { - char buf[4]; + dlog_print(s); + dlog_endline(); +} + +/** Print unsigned number with base */ +static void dlog_printub(unsigned int num, int base) +{ + char buf[8]; int i = 0; do { @@ -39,19 +64,15 @@ static inline void dlog_printu(unsigned int num, int base) i++; num /= base; - } while (num > 0); + } while (num > 0 && i < sizeof(buf)); 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) +/** Print signed number with base */ +static void dlog_printdb(int num, int base) { unsigned int unum; @@ -63,13 +84,25 @@ static inline void dlog_printd(int num, int base) unum = num; } - dlog_printu(unum, base); + dlog_printub(unum, base); } -static inline void dlog_puts(const char *s) +/** Print unsigned number in base 16. */ +static void dlog_printu(unsigned int num) { - dlog_print(s); - dlog_endline(); + dlog_printub(num, 10); +} + +/** Print unsigned number in base 10. */ +static void dlog_printx(unsigned int num) +{ + dlog_printub(num, 16); +} + +/** Print signed number in base 10. */ +static void dlog_printd(int num) +{ + dlog_printdb(num, 10); } #else @@ -77,11 +110,14 @@ static inline void dlog_puts(const char *s) #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) +#define dlog_printub(n,b) +#define dlog_printdb(n,b) +#define dlog_printx(n) +#define dlog_printu(n) +#define dlog_printd(n) -#endif -#endif +#endif /* ENABLE_DLOG */ + +#endif /* DLOG_H */ diff --git a/dosmain.c b/dosmain.c index 36bea69..646a03a 100644 --- a/dosmain.c +++ b/dosmain.c @@ -33,6 +33,7 @@ static unsigned get_resident_size(void) return FP_OFF(&resident_end); } +#if USE_VIRTUALBOX static int set_integration(LPTSRDATA data, bool enable) { if (enable) { @@ -42,7 +43,7 @@ static int set_integration(LPTSRDATA data, bool enable) 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); + printf("Physical address used 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"); @@ -70,28 +71,39 @@ static int set_integration(LPTSRDATA data, bool enable) 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; + + return 0; } +#endif static int configure_driver(LPTSRDATA data) { int err; // First check for PS/2 mouse availability - if ((err = ps2m_init(PS2_MOUSE_PLAIN_PACKET_SIZE))) { + if ((err = ps2m_init(PS2M_PACKET_SIZE_PLAIN))) { fprintf(stderr, "Cannot init PS/2 mouse BIOS, err=%d\n", err); // Can't do anything without PS/2 return err; } + // Let's utilize the wheel by default + data->usewheel = true; + + if (data->usewheel) { + // Do a quick check for a mouse wheel here. + // The TSR will do its own check when it is reset anyway + if (data->haswheel = ps2m_detect_wheel()) { + printf("Wheel mouse found and enabled\n"); + } + } + #if USE_VIRTUALBOX // Assume initially that we want integration and host cursor set_integration(data, true); @@ -118,9 +130,15 @@ static int install_driver(LPTSRDATA data) deallocate_environment(_psp); data->prev_int33_handler = _dos_getvect(0x33); - _dos_setvect(0x33, int33_isr); +#if USE_INT2F + data->prev_int2f_handler = _dos_getvect(0x2f); + _dos_setvect(0x2f, int2f_isr); +#endif + + printf("Driver installed\n"); + _dos_keep(EXIT_SUCCESS, (256 + resident_size + 15) / 16); return 0; } @@ -131,10 +149,22 @@ static bool check_if_driver_uninstallable(LPTSRDATA data) 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"); + fprintf(stderr, "INT33 has been hooked by some other driver, removing anyway (will likely crash soon)\n"); return true; } +#if USE_INT2F + { + void (__interrupt __far *cur_int2f_handler)() = _dos_getvect(0x33); + void (__interrupt __far *our_int2f_handler)() = MK_FP(FP_SEG(data), FP_OFF(int2f_isr)); + + if (cur_int2f_handler != our_int2f_handler) { + fprintf(stderr, "INT2F has been hooked by some other driver, removing anyway (will likely crash soon)\n"); + return true; + } + } +#endif + return true; } @@ -156,6 +186,10 @@ static int uninstall_driver(LPTSRDATA data) { _dos_setvect(0x33, data->prev_int33_handler); +#if USE_INT2F + _dos_setvect(0x2f, data->prev_int2f_handler); +#endif + // 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); @@ -169,12 +203,22 @@ static unsigned int33_call(unsigned ax); #pragma aux int33_call parm [ax] value [ax] = \ "int 0x33"; +static unsigned int2f_call(unsigned ax); +#pragma aux int2f_call parm [ax] value [ax] = \ + "int 0x2f"; + static int driver_reset(void) { printf("Reset mouse driver\n"); return int33_call(0x0) == 0xFFFF; } +static int driver_test(void) +{ + int2f_call(0x1234); + return EXIT_FAILURE; +} + static int driver_not_found(void) { fprintf(stderr, "Driver data not found (driver not installed?)\n"); @@ -189,8 +233,10 @@ static void print_help(void) "Supported actions:\n" "\tinstall install the driver (default)\n" "\tuninstall uninstall the driver from memory\n" +#if USE_VIRTUALBOX "\tinteg enable/disable virtualbox integration\n" "\thostcur enable/disable mouse cursor rendering in host\n" +#endif "\treset reset mouse driver settings\n" ); } @@ -222,7 +268,7 @@ 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); + printf("\nVBMouse 0.x (MSMOUSE %x.%x)\n", REPORTED_VERSION_MAJOR, REPORTED_VERSION_MINOR); if (argi >= argc || stricmp(argv[argi], "install") == 0) { if (data) { @@ -244,6 +290,7 @@ int main(int argc, const char *argv[]) return EXIT_FAILURE; } return uninstall_driver(data); +#if USE_VIRTUALBOX } else if (stricmp(argv[argi], "integ") == 0) { bool enable = true; @@ -266,8 +313,11 @@ int main(int argc, const char *argv[]) } return set_host_cursor(data, enable); +#endif } else if (stricmp(argv[argi], "reset") == 0) { return driver_reset(); + } else if (stricmp(argv[argi], "test") == 0) { + return driver_test(); } print_help(); diff --git a/dostsr.c b/dostsr.c index b4d517a..4ec9975 100644 --- a/dostsr.c +++ b/dostsr.c @@ -38,13 +38,6 @@ static const uint16_t default_cursor_graphic[] = { 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; @@ -53,6 +46,202 @@ static void bound_position_to_window(void) if (data.pos.y > data.max.y) data.pos.y = data.max.y; } +static inline bool is_text_mode(uint8_t mode) +{ + switch (mode) { + case 0: + case 1: + case 2: + case 3: // CGA text modes with 25 rows and variable columns + case 7: // MDA Mono text mode + return true; + default: + return false; + } +} + +static void hide_text_cursor(void) +{ + // 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; +} + +static void show_text_cursor(void) +{ + 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; +} + +static bool get_graphic_cursor_area(struct point __far *cursor_pos, + struct point __far *start, + struct point __far *size, + struct point __far *offset) +{ + struct point screen_size; + screen_size.x = ((data.screen_max.x + 1) / data.screen_scale.x); + screen_size.y = ((data.screen_max.y + 1) / data.screen_scale.y); + + start->x = (cursor_pos->x / data.screen_scale.x) - data.cursor_hotspot.x; + start->y = (cursor_pos->y / data.screen_scale.y) - data.cursor_hotspot.y; + size->x = GRAPHIC_CURSOR_WIDTH; + size->y = GRAPHIC_CURSOR_HEIGHT; + offset->x = 0; + offset->y = 0; + + // Start clipping around + if (start->x < 0) { + offset->x += -start->x; + start->x = 0; + } + if (start->y < 0) { + offset->y += -start->y; + start->y = 0; + } + if (start->x > screen_size.x) { + return false; // Don't render cursor + } else if (start->x + size->x > screen_size.x) { + size->x -= (start->x + size->x) - screen_size.x; + } + if (start->y > screen_size.y) { + return false; + } else if (start->y + size->y > screen_size.y) { + size->y -= (start->y + size->y) - screen_size.y; + } + + return true; +} + +static inline uint8_t * get_prev_graphic_cursor_scanline(unsigned y) +{ + return &data.cursor_prev_graphic[y * GRAPHIC_CURSOR_WIDTH]; +} + +static inline uint16_t get_graphic_cursor_and_mask(unsigned y) +{ + return data.cursor_graphic[y]; +} + +static inline uint16_t get_graphic_cursor_xor_mask(unsigned y) +{ + return data.cursor_graphic[GRAPHIC_CURSOR_HEIGHT + y]; +} + +static void hide_graphic_cursor(void) +{ + uint8_t __far *pixel; + uint8_t *cursor_prev; + struct point start, size, offset; + unsigned pixels_per_byte, y, x; + + // Compute the area where the cursor is currently positioned + if (!get_graphic_cursor_area(&data.cursor_pos, &start, &size, &offset)) { + return; + } + + switch (data.screen_mode) { + case 4: + case 5: + case 6: // CGA modes + pixels_per_byte = data.screen_mode == 0x6 ? 8 : 4; + + for (y = 0; y < size.y; y++) { + cursor_prev = get_prev_graphic_cursor_scanline(offset.y + y); + pixel = get_video_scanline(data.screen_mode, data.screen_page, start.y + y) + + start.x / pixels_per_byte; + + // Restore this scaline from cursor_prev + nfmemcpy(pixel, cursor_prev, + ((start.x % pixels_per_byte) + size.x + (pixels_per_byte - 1)) / pixels_per_byte); + } + + break; + } + + data.cursor_visible = false; +} + +static void show_graphic_cursor(void) +{ + static const uint16_t msb_mask = 0x8000; + uint16_t cursor_and_mask, cursor_xor_mask; + uint8_t __far *pixel, pixel_mask; + uint8_t *cursor_prev; + struct point start, size, offset; + unsigned pixels_per_byte, x, y; + + // Compute the area where the cursor is supposed to be drawn + if (!get_graphic_cursor_area(&data.pos, &start, &size, &offset)) { + return; + } + + switch (data.screen_mode) { + case 4: + case 5: + case 6: // CGA modes + pixels_per_byte = data.screen_mode == 0x6 ? 8 : 4; + + for (y = 0; y < size.y; y++) { + cursor_and_mask = get_graphic_cursor_and_mask(offset.y + y) << offset.x; + cursor_xor_mask = get_graphic_cursor_xor_mask(offset.y + y) << offset.y; + cursor_prev = get_prev_graphic_cursor_scanline(offset.y + y); + pixel = get_video_scanline(data.screen_mode, data.screen_page, start.y + y) + + start.x / pixels_per_byte; + + // First copy this scanline to cursor_prev before any changes + fnmemcpy(cursor_prev, pixel, + ((start.x % pixels_per_byte) + size.x + (pixels_per_byte - 1)) / pixels_per_byte); + + // pixel points the previous multiple of pixels_per_byte; + // now advance to the start of cursor while updating the pixel_mask + pixel_mask = pixels_per_byte == 8 ? 0x80 : 0xC0; + for (x = 0; x < (start.x % pixels_per_byte); x++) { + pixel_mask >>= 8 / pixels_per_byte; + } + + // Now pixel points to the start of cursor + for (; x < (start.x % pixels_per_byte) + size.x; x++) { + uint8_t rest = *pixel & ~pixel_mask; + + if (!(cursor_and_mask & msb_mask)) { + *pixel = rest; + } + if (cursor_xor_mask & msb_mask) { + *pixel = rest | (*pixel ^ 0xFF) & pixel_mask; + } + + // Advance to the next pixel + if (x % pixels_per_byte == pixels_per_byte - 1) { + // Next iteration starts new byte, reload pixel_mask + pixel++; + pixel_mask = pixels_per_byte == 8 ? 0x80 : 0xC0; + } else { + pixel_mask >>= 8 / pixels_per_byte; + } + + // Advance to the next bit in the cursor mask + cursor_and_mask <<= 1; + cursor_xor_mask <<= 1; + } + } + break; + default: + dlog_print("Graphic mode 0x"); + dlog_printx(data.screen_mode); + dlog_puts(" not supported"); + return; + } + + data.cursor_pos = data.pos; + data.cursor_visible = true; +} + /** Refreshes cursor position and visibility. */ static void refresh_cursor(void) { @@ -74,25 +263,45 @@ static void refresh_cursor(void) } #endif - if (data.screen_text_mode) { + if (is_text_mode(data.screen_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; + // Hide the cursor at the old position if any + hide_text_cursor(); + } + if (should_show) { + // Show the cursor at the new position + show_text_cursor(); + } + } else { + if (data.cursor_visible) { + hide_graphic_cursor(); + } + if (should_show) { + show_graphic_cursor(); + } + } +} + +/** Forcefully hides the mouse cursor if shown. */ +static void hide_cursor(void) +{ +#if USE_VIRTUALBOX + if (data.vbwantcursor) { + vbox_set_pointer_visible(&data.vb, false); + if (data.vbhaveabs) { data.cursor_visible = false; } + } +#endif - 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; + if (is_text_mode(data.screen_mode)) { + if (data.cursor_visible) { + hide_text_cursor(); } } else { - dlog_puts("Graphic mode cursor is not implemented"); + if (data.cursor_visible) { + hide_graphic_cursor(); + } } } @@ -101,11 +310,11 @@ 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); + const unsigned width = GRAPHIC_CURSOR_WIDTH, height = GRAPHIC_CURSOR_HEIGHT; + const unsigned and_mask_size = (width + 7) / 8 * height; + const unsigned xor_mask_size = width * height * 4; + const unsigned data_size = and_mask_size + xor_mask_size; + const unsigned full_size = MAX(sizeof(VMMDevReqMousePointer), 24 + 20 + data_size); unsigned int offset = 0, y, x; bzero(req, full_size); @@ -121,17 +330,17 @@ static void load_cursor(void) req->width = width; req->height = height; - // Just byteswap the and mask + // Just byteswap the AND mask for (y = 0; y < height; ++y) { - uint16_t line = data.cursor_graphic[y]; + uint16_t line = get_graphic_cursor_and_mask(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. + // But the XOR mask needs to be converted to huge 4-byte "RGBA" format. for (y = 0; y < height; ++y) { - uint16_t line = data.cursor_graphic[height + y]; + uint16_t line = get_graphic_cursor_xor_mask(y); for (x = 0; x < width; ++x) { unsigned int pos = offset + (y * width * 4) + (x*4) + 0; @@ -154,8 +363,9 @@ static void load_cursor(void) dlog_puts("Could not send cursor to VirtualBox"); } - // VirtualBox shows the cursor even if we don't want; - // rehide if necessary. + // After we send this message, it looks like VirtualBox shows the cursor + // even if we didn't actually want it to be visible at this point. + // Mark it as visible so that refresh_cursor() will rehide it if necessary. data.cursor_visible = true; refresh_cursor(); } @@ -164,9 +374,8 @@ static void load_cursor(void) 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); + uint8_t screen_columns = bda_get_num_columns(); + uint8_t mode = bda_get_video_mode() & ~0x80; bool mode_change = mode != data.screen_mode; if (mode_change && data.cursor_visible) { @@ -177,69 +386,79 @@ static void refresh_video_info(void) dlog_print("Current video mode="); dlog_printx(mode); dlog_print(" with cols="); - dlog_printd(screen_columns, 10); + dlog_printd(screen_columns); dlog_endline(); data.screen_mode = mode; - data.screen_page = video_page; + data.screen_page = bda_get_cur_video_page(); + data.screen_scale.x = 1; + data.screen_scale.y = 1; + // Compute screen coordinates which are used to events from + // absolute mouse coordinates. switch (mode) { case 0: case 1: case 2: - case 3: /* VGA text modes with 25 rows and variable columns */ + case 3: // CGA text modes with 25 rows and variable columns + case 7: // MDA Mono text mode 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 */ + case 5: // CGA low-res modes + case 0xd: // EGA low-res mode data.screen_max.x = 640 - 1; data.screen_max.y = 200 - 1; - data.screen_text_mode = false; + data.screen_scale.x = 2; // Really 320x200 + break; + + case 6: // CGA hi-res mode + case 0xe: // EGA modes + case 0x13: // VGA 256color mode + data.screen_max.x = 640 - 1; + data.screen_max.y = 200 - 1; + break; + + case 0xf: + case 0x10: // EGA 640x350 modes + data.screen_max.x = 640 - 1; + data.screen_max.y = 350 - 1; break; case 0x11: - case 0x12: - case 0x13: /* Graphical 640x480 modes. */ + case 0x12: // VGA 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; + // Unknown mode; assume default coordinates + // Note that if program sets up larger window coordinates, we'll use that instead. + data.screen_max.x = 640 - 1; + data.screen_max.y = 200 - 1; 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 +#if TRACE_EVENT dlog_print("calling event handler events="); dlog_printx(events); dlog_print(" buttons="); dlog_printx(buttons); dlog_print(" x="); - dlog_printd(x, 10); + dlog_printd(x); dlog_print(" y="); - dlog_printd(y, 10); + dlog_printd(y); dlog_print(" dx="); - dlog_printd(delta_x, 10); + dlog_printd(delta_x); dlog_print(" dy="); - dlog_printd(delta_y, 10); + dlog_printd(delta_y); dlog_endline(); #endif @@ -255,41 +474,53 @@ static void call_event_handler(void (__far *handler)(), uint16_t events, } } -static void handle_mouse_event(uint8_t buttons, bool absolute, int x, int y, int z) +static void handle_mouse_event(uint16_t buttons, bool absolute, int x, int y, int z) { uint16_t events = 0; int i; -#if TOO_VERBOSE +#if TRACE_EVENT dlog_print("handle mouse event"); if (absolute) dlog_print(" absolute"); dlog_print(" buttons="); dlog_printx(buttons); dlog_print(" x="); - dlog_printd(x, 10); + dlog_printd(x); dlog_print(" y="); - dlog_printd(y, 10); + dlog_printd(y); + dlog_print(" z="); + dlog_printd(z); 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 { + events |= INT33_EVENT_MASK_ABSOLUTE; + + if (x != data.pos.x || y != data.pos.y) { + events |= INT33_EVENT_MASK_MOVEMENT; + + // Simulate a fake relative movement in mickeys + // This is almost certainly broken. + // Programs that expect relative movement data + // will almost never set a mickeyPerPixel value. + // So all we can do is guess. + data.delta.x += (x - data.pos.x) * 8; + data.delta.y += (y - data.pos.y) * 8; + + // Store the new absolute position + data.pos.x = x; + data.pos.y = y; + data.pos_frac.x = 0; + data.pos_frac.y = 0; + } + } else if (x || y) { // Relative movement: x,y are in mickeys uint16_t ticks = bda_get_tick_count_lo(); unsigned ax = ABS(x), ay = ABS(y); + events |= INT33_EVENT_MASK_MOVEMENT; + // Check if around one second has passed if ((ticks - data.last_ticks) >= 18) { data.total_motion = 0; @@ -306,18 +537,20 @@ static void handle_mouse_event(uint8_t buttons, bool absolute, int x, int y, int data.delta.x += x; data.delta.y += y; + data.delta_frac.x = 0; + data.delta_frac.y = 0; - 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); + // Convert mickeys into pixels + data.pos.x += scalei_rem(x, data.mickeysPerLine.x, 8, &data.pos_frac.x); + data.pos.y += scalei_rem(y, data.mickeysPerLine.y, 8, &data.pos_frac.y); } - bound_position_to_window(); - // TODO: Wheel - (void) z; + bound_position_to_window(); - // Report movement if there was any - if (data.delta.x || data.delta.y) { - events |= INT33_EVENT_MASK_MOVEMENT; + if (data.haswheel && z) { + events |= INT33_EVENT_MASK_WHEEL_MOVEMENT; + // Higher byte of buttons contains wheel movement + buttons |= (z & 0xFF) << 8; } // Update button status @@ -337,25 +570,16 @@ static void handle_mouse_event(uint8_t buttons, bool absolute, int x, int y, int 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; - } + 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; - } } } @@ -365,17 +589,18 @@ static void __far ps2_mouse_callback(uint8_t status, uint8_t x, uint8_t y, uint8 int sx = status & PS2M_STATUS_X_NEG ? 0xFF00 | x : x; int sy = -(status & PS2M_STATUS_Y_NEG ? 0xFF00 | y : y); + int sz = z; bool abs = false; -#if TOO_VERBOSE +#if TRACE_EVENT dlog_print("ps2 callback status="); dlog_printx(status); dlog_print(" sx="); - dlog_printd(sx, 10); + dlog_printd(sx); dlog_print(" sy="); - dlog_printd(sy, 10); - dlog_print(" z="); - dlog_printd(z, 10); + dlog_printd(sy); + dlog_print(" sz="); + dlog_printd(z); dlog_endline(); #endif @@ -394,8 +619,15 @@ static void __far ps2_mouse_callback(uint8_t status, uint8_t x, uint8_t y, uint8 } #endif + // VirtualBox/Bochs BIOS does not pass wheel data to the callback, + // so we will fetch it directly from the BIOS data segment. + if (data.haswheel && !sz) { + int8_t __far * mouse_packet = MK_FP(bda_get_ebda_segment(), 0x28); + sz = mouse_packet[3]; + } + handle_mouse_event(status & (PS2M_STATUS_BUTTON_1 | PS2M_STATUS_BUTTON_2 | PS2M_STATUS_BUTTON_3), - abs, sx, sy, z); + abs, sx, sy, sz); } #if USE_VIRTUALBOX @@ -425,8 +657,14 @@ static void reset_mouse_hardware() load_cursor(); #endif - ps2m_init(PS2_MOUSE_PLAIN_PACKET_SIZE); - ps2m_reset(); + if (data.usewheel && ps2m_detect_wheel()) { + // Detect wheel also reinitializes the mouse to the proper packet size + data.haswheel = true; + } else { + // Otherwise do an extra reset to return back to initial state, just in case + data.haswheel = false; + ps2m_init(PS2M_PACKET_SIZE_PLAIN); + } ps2m_set_resolution(3); // 3 = 200 dpi, 8 counts per millimeter ps2m_set_sample_rate(4); // 4 = 80 reports per second @@ -455,7 +693,7 @@ static void reset_mouse_settings() 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); + nnmemcpy(data.cursor_graphic, default_cursor_graphic, sizeof(data.cursor_graphic)); refresh_cursor(); // This will hide the cursor and update data.cursor_visible } @@ -465,10 +703,12 @@ static void reset_mouse_state() int i; data.pos.x = data.min.x; data.pos.y = data.min.y; + data.pos_frac.x = 0; + data.pos_frac.y = 0; data.delta.x = 0; data.delta.y = 0; - data.delta.x = 0; - data.delta.y = 0; + data.delta_frac.x = 0; + data.delta_frac.y = 0; data.buttons = 0; for (i = 0; i < NUM_BUTTONS; i++) { data.button[i].pressed.count = 0; @@ -482,6 +722,7 @@ static void reset_mouse_state() data.cursor_pos.x = 0; data.cursor_pos.y = 0; data.cursor_prev_char = 0; + bzero(data.cursor_prev_graphic, sizeof(data.cursor_prev_graphic)); } static void return_clear_button_counter(union INTPACK __far *r, struct buttoncounter *c) @@ -523,8 +764,12 @@ static void int33_handler(union INTPACK r) case INT33_SET_MOUSE_POSITION: data.pos.x = r.x.cx; data.pos.y = r.x.dx; + data.pos_frac.x = 0; + data.pos_frac.y = 0; data.delta.x = 0; data.delta.y = 0; + data.delta_frac.x = 0; + data.delta_frac.y = 0; bound_position_to_window(); break; case INT33_GET_BUTTON_PRESSED_COUNTER: @@ -537,9 +782,9 @@ static void int33_handler(union INTPACK r) break; case INT33_SET_HORIZONTAL_WINDOW: dlog_print("Mouse set horizontal window ["); - dlog_printd(r.x.cx, 10); + dlog_printd(r.x.cx); dlog_putc(','); - dlog_printd(r.x.dx, 10); + dlog_printd(r.x.dx); dlog_puts("]"); // Recheck in case someone changed the video mode refresh_video_info(); @@ -549,9 +794,9 @@ static void int33_handler(union INTPACK r) break; case INT33_SET_VERTICAL_WINDOW: dlog_print("Mouse set vertical window ["); - dlog_printd(r.x.cx, 10); + dlog_printd(r.x.cx); dlog_putc(','); - dlog_printd(r.x.dx, 10); + dlog_printd(r.x.dx); dlog_puts("]"); refresh_video_info(); data.min.y = r.x.cx; @@ -560,6 +805,7 @@ static void int33_handler(union INTPACK r) break; case INT33_SET_GRAPHICS_CURSOR: dlog_puts("Mouse set graphics cursor"); + hide_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); @@ -568,8 +814,9 @@ static void int33_handler(union INTPACK r) break; case INT33_SET_TEXT_CURSOR: dlog_print("Mouse set text cursor "); - dlog_printd(r.x.bx, 10); + dlog_printd(r.x.bx); dlog_endline(); + hide_cursor(); data.cursor_text_type = r.x.bx; data.cursor_text_and_mask = r.x.cx; data.cursor_text_xor_mask = r.x.dx; @@ -592,16 +839,16 @@ static void int33_handler(union INTPACK r) break; case INT33_SET_MOUSE_SPEED: dlog_print("Mouse set speed x="); - dlog_printd(r.x.cx, 10); + dlog_printd(r.x.cx); dlog_print(" y="); - dlog_printd(r.x.dx, 10); + dlog_printd(r.x.dx); 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_printd(r.x.dx); dlog_endline(); data.doubleSpeedThreshold = r.x.dx; break; @@ -629,11 +876,11 @@ static void int33_handler(union INTPACK r) break; case INT33_SET_MOUSE_SENSITIVITY: dlog_print("Mouse set speed x="); - dlog_printd(r.x.bx, 10); + dlog_printd(r.x.bx); dlog_print(" y="); - dlog_printd(r.x.cx, 10); + dlog_printd(r.x.cx); dlog_print(" threshold="); - dlog_printd(r.x.dx, 10); + dlog_printd(r.x.dx); dlog_endline(); data.mickeysPerLine.x = r.x.bx; data.mickeysPerLine.y = r.x.cx; @@ -657,8 +904,8 @@ static void int33_handler(union INTPACK r) 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.bh = REPORTED_VERSION_MAJOR; + r.h.bl = REPORTED_VERSION_MINOR; r.h.ch = INT33_MOUSE_TYPE_PS2; r.h.cl = 0; break; @@ -710,6 +957,53 @@ void __declspec(naked) __far int33_isr(void) } } +#if USE_INT2F +static void int2f_handler(union INTPACK r) +#pragma aux int2f_handler "*" parm caller [] modify [ax bx cx dx es] +{ +} + +void __declspec(naked) __far int2f_isr(void) +{ + __asm { + ; Space for the pointer to the next ISR in the chain + push dword ptr 0 + + ; Save all registers (also acts as the INTPACK paramenter later) + pusha + push ds + push es + push fs + push gs + + mov bp, sp + push cs + pop ds + + ; Load the address of the next ISR in the chain + ; Stack looks like sp+0: gs, fs, es, ds (4*2bytes) + ; sp+8: pusha (8 * 2bytes) + ; sp+24: dword ptr (the space we reserved to store the next ISR) + mov ax, word ptr [data + 4] ; i.e. data.prev_int2f_handler -- Watcom doesn't support structs + mov [bp + 24], ax + mov ax, word ptr [data + 6] ; i.e. data.prev_int2f_handler[2] + mov [bp + 26], ax + + ; Now call our handler + call int2f_handler + + pop gs + pop fs + pop es + pop ds + popa + + ; This will jump to the address of the next ISR we loaded before + retf + } +} +#endif + static LPTSRDATA int33_get_tsr_data(void); #pragma aux int33_get_tsr_data = \ "xor ax, ax" \ diff --git a/dostsr.h b/dostsr.h index c808709..d7c8183 100644 --- a/dostsr.h +++ b/dostsr.h @@ -26,11 +26,16 @@ #include "vbox.h" #define USE_VIRTUALBOX 1 +#define USE_INT2F 1 +#define TRACE_EVENT 1 #define NUM_BUTTONS 3 -#define DRIVER_VERSION_MAJOR 8 -#define DRIVER_VERSION_MINOR 0x20 +#define GRAPHIC_CURSOR_WIDTH 16 +#define GRAPHIC_CURSOR_HEIGHT 16 + +#define REPORTED_VERSION_MAJOR 8 +#define REPORTED_VERSION_MINOR 0x20 struct point { int16_t x, y; @@ -40,32 +45,60 @@ typedef struct tsrdata { // TSR installation data /** Previous int33 ISR, storing it for uninstall. */ void (__interrupt __far *prev_int33_handler)(); +#if USE_INT2F + void (__interrupt __far *prev_int2f_handler)(); +#endif + /** Whether to enable & use wheel mouse. */ + bool usewheel; // Video settings + /** Current video mode. */ uint8_t screen_mode; + /** Active video page. */ uint8_t screen_page; - bool screen_text_mode; + /** Max (virtual) coordinates of full screen in the current mode. + * Used for rendering graphic cursor, mapping absolute coordinates, + * and initializing the default min/max window. */ struct point screen_max; + /** Some graphic modes have pixel doubling, so the virtual coordinates + * are double vs real framebuffer coordinates. */ + struct point screen_scale; + + // Detected mouse hardware + /** Whether the current mouse has a wheel (and support is enabled). */ + bool haswheel; // Current mouse settings + /** Mouse sensitivity/speed. */ struct point mickeysPerLine; // mickeys per 8 pixels + /** Mouse acceleration "double-speed threshold". */ uint16_t doubleSpeedThreshold; // mickeys + /** Current window min coordinates. */ struct point min; + /** Current window max coordinates. */ struct point max; + /** Current cursor visible counter. If >= 0, cursor should be shown. */ int16_t visible_count; + /** For text cursor, whether this is a software or hardware cursor. */ uint8_t cursor_text_type; + /** Masks for the text cursor. */ uint16_t cursor_text_and_mask, cursor_text_xor_mask; + /** Hotspot for the graphic cursor. */ struct point cursor_hotspot; - uint16_t cursor_graphic[16+16]; + /** Masks for the graphic cursor. */ + uint16_t cursor_graphic[GRAPHIC_CURSOR_HEIGHT*2]; // 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). */ + * (8ths of pixel). */ struct point pos_frac; /** Current delta movement (in mickeys) since the last report. */ struct point delta; + /** Current remainder of delta movement that does not yet translate to an entire mickey + * Usually only when mickeysPerLine is not a multiple of 8. */ + struct point delta_frac; /** Total mickeys moved in the last second. */ uint16_t total_motion; /** Ticks when the above value was last reset. */ @@ -78,14 +111,23 @@ typedef struct tsrdata { uint16_t count; } pressed, released; } button[NUM_BUTTONS]; + + // Cursor information /** Whether the cursor is currently displayed or not. */ bool cursor_visible; + /** The current position at which the cursor is displayed. */ struct point cursor_pos; + /** For text mode cursor, the character data that was displayed below the cursor. */ uint16_t cursor_prev_char; + /** For graphical mode cursor, contents of the screen that were displayed below + * the cursor before the cursor was drawn. */ + uint8_t cursor_prev_graphic[GRAPHIC_CURSOR_WIDTH * GRAPHIC_CURSOR_HEIGHT]; // Current handlers + /** Address of the event handler. */ void (__far *event_handler)(); - uint8_t event_mask; + /** Events for which we should call the event handler. */ + uint16_t event_mask; #if USE_VIRTUALBOX /** VirtualBox is available. */ @@ -103,6 +145,8 @@ typedef TSRDATA __far * LPTSRDATA; extern void __declspec(naked) __far int33_isr(void); +extern void __declspec(naked) __far int2f_isr(void); + extern LPTSRDATA __far get_tsr_data(bool installed); extern int resident_end; diff --git a/int10vga.h b/int10vga.h index f3e0a07..e254e3f 100644 --- a/int10vga.h +++ b/int10vga.h @@ -5,6 +5,7 @@ #define BIOS_DATA_AREA_SEGMENT 0x40 +// TODO Assuming screenwidth, videopage on ss rather than using far static uint8_t int10_get_video_mode(uint8_t *screenwidth, uint8_t *videopage); #pragma aux int10_get_video_mode = \ "mov al, 0" \ @@ -16,6 +17,11 @@ static uint8_t int10_get_video_mode(uint8_t *screenwidth, uint8_t *videopage); __value [al] \ __modify [ax bh] +static inline uint32_t bda_get_dword(unsigned int offset) { + uint32_t __far *p = MK_FP(BIOS_DATA_AREA_SEGMENT, offset); + return *p; +} + static inline uint16_t bda_get_word(unsigned int offset) { uint16_t __far *p = MK_FP(BIOS_DATA_AREA_SEGMENT, offset); return *p; @@ -26,10 +32,14 @@ static inline uint8_t bda_get_byte(unsigned int offset) { return *p; } +#define bda_get_ebda_segment() bda_get_word(0x0e) + #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_cur_video_page() bda_get_word(0x62) + +#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) @@ -38,4 +48,23 @@ static inline uint16_t __far * get_video_char(uint8_t page, unsigned int x, unsi + ((y * bda_get_num_columns()) + x) * 2); } +static inline uint8_t __far * get_video_scanline(uint8_t mode, uint8_t page, unsigned int y) +{ + switch (mode) { + case 4: + case 5: + case 6: // CGA modes + // Scalines are 80 bytes long, however they are interleaved. + // Even numbered scanlines begin at 0, odd lines begin at 0x2000. + // So offset by y%2 * 0x2000. + return MK_FP(0xB800, (page * bda_get_video_page_size()) + + ((y/2) * 80) + (y%2) * 0x2000); + + default: + return 0; + } +} + + + #endif diff --git a/int2fwin.h b/int2fwin.h index 7845293..e8540dc 100644 --- a/int2fwin.h +++ b/int2fwin.h @@ -24,10 +24,27 @@ #include #include +struct win386_instance_item +{ + void __far * ptr; + uint16_t size; +}; + +struct win386_startup_info +{ + uint16_t version; + struct win386_startup_info __far * next; + char __far * device_driver; + void __far * device_driver_data; + struct win386_instance_item __far *instance_data; +}; + typedef void (__far *LPFN)(void); enum int2f_functions { + INT2F_NOTIFY_WIN386_STARTUP = 0x1605, + INT2F_NOTIFY_BACKGROUND_SWITCH = 0x4001, INT2F_NOTIFY_FOREGROUND_SWITCH = 0x4002 }; diff --git a/int33.h b/int33.h index c31fc2c..f97ba21 100644 --- a/int33.h +++ b/int33.h @@ -162,7 +162,15 @@ enum INT33_EVENT_MASK { INT33_EVENT_MASK_CENTER_BUTTON_PRESSED = 1 << 5, INT33_EVENT_MASK_CENTER_BUTTON_RELEASED = 1 << 6, - INT33_EVENT_MASK_ALL = 0xFF + // Wheel API Extensions: + /** Wheel mouse movement. */ + INT33_EVENT_MASK_WHEEL_MOVEMENT = 1 << 7, + + // Absolute API extensions: + /** The source of the event is an absolute pointing device. */ + INT33_EVENT_MASK_ABSOLUTE = 1 << 8, + + INT33_EVENT_MASK_ALL = 0xFFFF }; #pragma aux INT33_CB far loadds parm [ax] [bx] [cx] [dx] [si] [di] diff --git a/makefile b/makefile index 14e60bc..2f4d78a 100644 --- a/makefile +++ b/makefile @@ -2,15 +2,17 @@ # 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 +doscflags = -bt=dos -ms -6 -os -w3 +# -ms to use small memory model (though sometimes ss != ds...) # -os to optimize for size +dostsrcflags = -zu -s -g=RES_GROUP -nd=RES -nt=RES_TEXT -nc=RES_CODE +# -s to disable stack checks, since it inserts calls to the runtime from the TSR part +# -zu since ss != ds on the TSR 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) +# -mc to use compact memory model (far data pointers, ss != ds always) # -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) @@ -23,13 +25,13 @@ 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 $[@ + wcc -fo=$^@ $(doscflags) $(dostsrcflags) $[@ dosmain.obj: dosmain.c .AUTODEPEND wcc -fo=$^@ $(doscflags) $[@ vbox.obj: vbox.c .AUTODEPEND - wcc -fo=$^@ $(doscflags) $[@ + wcc -fo=$^@ $(doscflags) $[@ vbmouse.drv: w16mouse.lnk $(w16objs) wlink @$[@ name $@ file { $(w16objs) } diff --git a/ps2.h b/ps2.h index e5304c4..717ab84 100644 --- a/ps2.h +++ b/ps2.h @@ -28,9 +28,6 @@ /** The corresponding interrupt vector for IRQ 12. */ #define PS2_MOUSE_INT_VECTOR 0x74 -/** Packet size for plain PS/2 in default protocol. */ -#define PS2_MOUSE_PLAIN_PACKET_SIZE 3 - typedef uint8_t ps2m_err; enum ps2m_errors { PS2M_ERR_INVALID_FUNCTION = 1, @@ -50,6 +47,11 @@ enum ps2m_status { PS2M_STATUS_Y_OVF = 1 << 7, }; +enum ps2m_packet_size { + PS2M_PACKET_SIZE_PLAIN = 3, + PS2M_PACKET_SIZE_EXT = 4, +}; + enum ps2m_device_ids { /** Standard PS/2 mouse, 2 buttons. */ PS2M_DEVICE_ID_PLAIN = 0, @@ -61,6 +63,23 @@ enum ps2m_device_ids { PS2M_DEVICE_ID_IMEX_HORZ = 5 }; +enum ps2m_resolution { + PS2M_RESOLUTION_25 = 0, + PS2M_RESOLUTION_50 = 1, + PS2M_RESOLUTION_100 = 2, + PS2M_RESOLUTION_200 = 3 +}; + +enum ps2m_sample_rate { + PS2M_SAMPLE_RATE_10 = 0, + PS2M_SAMPLE_RATE_20 = 1, + PS2M_SAMPLE_RATE_40 = 2, + PS2M_SAMPLE_RATE_60 = 3, + PS2M_SAMPLE_RATE_80 = 4, + PS2M_SAMPLE_RATE_100 = 5, + PS2M_SAMPLE_RATE_200 = 6 +}; + #pragma aux PS2_CB far loadds parm reverse caller [] // TODO: ax and es look not be preserved with this. VBox BIOS already preserves them though, as well as 32-bit registers @@ -95,7 +114,7 @@ static ps2m_err ps2m_reset(void); __value [ah] \ __modify [ax] -static ps2m_err ps2m_get_device_id(uint8_t *device_id); +static ps2m_err ps2m_get_device_id(uint8_t __far *device_id); #pragma aux ps2m_get_device_id = \ "stc" \ "mov ax, 0xC204" /* Pointing device: get device ID */ \ @@ -110,10 +129,6 @@ 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" \ @@ -128,13 +143,6 @@ 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" \ @@ -193,4 +201,39 @@ static ps2m_err ps2m_enable(bool enable); __value [ah] \ __modify [ax] +static void ps2m_send_imps2_sequence(void) +{ + ps2m_set_sample_rate(PS2M_SAMPLE_RATE_200); + ps2m_set_sample_rate(PS2M_SAMPLE_RATE_100); + ps2m_set_sample_rate(PS2M_SAMPLE_RATE_80); +} + +static bool ps2m_detect_wheel(void) +{ + int err; + uint8_t device_id; + + // Switch to the 4-byte packet and reset the mouse. + // We are not supposed to receive messages at this time anyway. + err = ps2m_init(PS2M_PACKET_SIZE_EXT); + if (err) { + return false; + } + + // Get the initial mouse device id + err = ps2m_get_device_id(&device_id); + if (err || device_id != 0) { + // TODO : Likely have to accept more device_ids here. + return false; + } + + // Send the knock sequence to activate the extended packet + ps2m_send_imps2_sequence(); + + // Now check if the device id has changed + err = ps2m_get_device_id(&device_id); + + return err == 0 && device_id == PS2M_DEVICE_ID_IMPS2; +} + #endif diff --git a/utils.h b/utils.h index 4740644..39260c4 100644 --- a/utils.h +++ b/utils.h @@ -27,7 +27,17 @@ static unsigned scaleu(unsigned x, unsigned srcmax, unsigned dstmax); "div bx" /* ax = dx:ax / srcmax */\ __parm [ax] [bx] [cx] \ __value [ax] \ - __modify [ax cx dx] + __modify [ax dx] + +/** Map x linearly from range [0, srcmax] to [0, dstmax]. + * Equivalent of (x * dstmax) / srcmax but with 32-bit signed precision. */ +static int scalei(int x, int srcmax, int dstmax); +#pragma aux scalei = \ + "imul cx" /* dx:ax = x * dstmax */ \ + "idiv bx" /* ax = dx:ax / srcmax */ \ + __parm [ax] [bx] [cx] \ + __value [ax] \ + __modify [ax dx] /** Map x linearly from range [0, srcmax] to [0, dstmax]. * Equivalent of (x * dstmax) / srcmax but with 32-bit signed precision. @@ -36,13 +46,18 @@ static unsigned scaleu(unsigned x, unsigned srcmax, unsigned dstmax); static int scalei_rem(int x, int srcmax, int dstmax, short *rem); #pragma aux scalei_rem = \ "imul cx" /* dx:ax = x * dstmax */ \ + "mov cx, [si]" /* cx = rem */ \ + "test cx, cx" \ + "setns cl" /* cl = 1 if rem positive, 0 if negative */ \ + "movzx cx, cl" \ + "dec cx" /* cx = 0 if rem positive, -1 if negative */ \ "add ax, [si]" /* ax += *rem */ \ - "adc dx, 0" /* dx += 1 if carry */ \ + "adc dx, cx" /* dx += 1 if carry and rem positive, 0 if carry and rem negative (aka. signed addition) */ \ "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] + __modify [ax cx dx si] static void bzero(void __far *buf, unsigned int size); #pragma aux bzero = \ diff --git a/vbox.h b/vbox.h index 486058a..dfa124b 100644 --- a/vbox.h +++ b/vbox.h @@ -62,7 +62,7 @@ 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) +static int vbox_report_guest_info(LPVBOXCOMM vb, uint32_t osType) { VMMDevReportGuestInfo __far *req = (void __far *) vb->buf; @@ -81,7 +81,7 @@ static inline int vbox_report_guest_info(LPVBOXCOMM vb, uint32_t osType) } /** Tells VirtualBox whether we want absolute mouse information or not. */ -static inline int vbox_set_mouse(LPVBOXCOMM vb, bool absolute, bool pointer) +static int vbox_set_mouse(LPVBOXCOMM vb, bool absolute, bool pointer) { VMMDevReqMouseStatus __far *req = (void __far *) vb->buf; @@ -102,8 +102,8 @@ static inline int vbox_set_mouse(LPVBOXCOMM vb, bool absolute, bool pointer) /** 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) +static int vbox_get_mouse(LPVBOXCOMM vb, bool __far *abs, + uint16_t __far *xpos, uint16_t __far *ypos) { VMMDevReqMouseStatus __far *req = (void __far *) vb->buf; @@ -124,7 +124,7 @@ static inline int vbox_get_mouse(LPVBOXCOMM vb, bool *abs, } /** @todo */ -static inline int vbox_set_pointer_visible(LPVBOXCOMM vb, bool visible) +static int vbox_set_pointer_visible(LPVBOXCOMM vb, bool visible) { VMMDevReqMousePointer __far *req = (void __far *) vb->buf; @@ -142,45 +142,4 @@ static inline int vbox_set_pointer_visible(LPVBOXCOMM vb, bool visible) return req->header.rc; } -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; -} - -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); - - if (full_size >= VBOX_BUFFER_SIZE) { - return -2; - } - - 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 = xHot; - req->yHot = yHot; - req->width = width; - req->height = height; - - ffmemcpy(req->pointerData, data, data_size); - - vbox_send_request(vb->iobase, vb->buf_physaddr); - - return req->header.rc; -} - #endif diff --git a/w16mouse.c b/w16mouse.c index 93aadf5..f1ca962 100644 --- a/w16mouse.c +++ b/w16mouse.c @@ -19,12 +19,13 @@ #include -#include "dlog.h" #include "utils.h" #include "int33.h" #include "int2fwin.h" #include "w16mouse.h" +#define TRACE_EVENT 1 + /** If 1, hook int2f to detect fullscreen DOSBoxes and auto-disable this driver. */ #define HOOK_INT2F 0 @@ -33,14 +34,13 @@ /** 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 -}; +static struct mouseflags { + bool enabled : 1; + bool haswin386 : 1; + bool int2f_hooked : 1; +} flags; +/** Previous deltaX, deltaY from the int33 mouse callback (for relative motion) */ +static short prev_delta_x, prev_delta_y; #if HOOK_INT2F /** Existing interrupt2f handler. */ static LPFN prev_int2f_handler; @@ -54,35 +54,67 @@ static void send_event(unsigned short Status, short deltaX, short deltaY, short #pragma code_seg ( "CALLBACKS" ) +#include "dlog.h" + 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 TRACE_EVENT_IN + dlog_print("w16mouse: events="); + dlog_printx(events); + dlog_print(" buttons="); + dlog_printx(buttons); + dlog_print(" x="); + dlog_printd(x); + dlog_print(" y="); + dlog_printd(y); + dlog_print(" dx="); + dlog_printd(delta_x); + dlog_print(" dy="); + dlog_printd(delta_y); + dlog_endline(); +#endif + + 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; + status |= SF_MOVEMENT; } + if (events & INT33_EVENT_MASK_ABSOLUTE) { + status |= SF_ABSOLUTE; + + // We set the window to be 0..0x7FFF, so just scale to 0xFFFF + x = (uint16_t)(x) * 2; + y = (uint16_t)(y) * 2; + } else { + // Use mickeys for relative motion + x = delta_x - prev_delta_x; + y = delta_y - prev_delta_y; + + prev_delta_x = delta_x; + prev_delta_y = delta_y; + } + + // Unused (void) buttons; - (void) delta_x; - (void) delta_y; -#if 0 +#if TRACE_EVENT dlog_print("w16mouse: event status="); dlog_printx(status); dlog_print(" x="); - dlog_printx(x); + dlog_printd(x); dlog_print(" y="); - dlog_printx(y); + dlog_printd(y); dlog_endline(); #endif - send_event(status, (uint16_t)(x) * 2, (uint16_t)(y) * 2, MOUSE_NUM_BUTTONS, 0, 0); + send_event(status, x, (uint16_t)(y), MOUSE_NUM_BUTTONS, 0, 0); } #if HOOK_INT2F @@ -90,7 +122,7 @@ static void FAR int33_mouse_callback(uint16_t events, uint16_t buttons, int16_t 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)) { + if (!flags.enabled) { return; } @@ -169,18 +201,20 @@ BOOL FAR PASCAL LibMain(HINSTANCE hInstance, WORD wDataSegment, #if HOOK_INT2F // Check now whether we are running under protected mode windows if (windows_386_enhanced_mode()) { - mouseflags |= MOUSEFLAGS_HAS_WIN386; + flags.haswin386 = true; } #endif +#if 0 // 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) { + if (flags.haswin386) { 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); } } +#endif return 1; } @@ -204,9 +238,7 @@ VOID FAR PASCAL Enable(LPFN_MOUSEEVENT lpEventProc) eventproc = lpEventProc; sti(); - if (!(mouseflags & MOUSEFLAGS_ENABLED)) { - dlog_puts("w16mouse: enable"); - + if (!flags.enabled) { int33_reset(); int33_set_horizontal_window(0, 0x7FFF); @@ -214,17 +246,14 @@ VOID FAR PASCAL Enable(LPFN_MOUSEEVENT lpEventProc) int33_set_event_handler(INT33_EVENT_MASK_ALL, int33_mouse_callback); - dlog_puts("w16mouse: int33 enabled"); - - mouseflags |= MOUSEFLAGS_ENABLED; + flags.enabled = true; #if HOOK_INT2F - if ((mouseflags & MOUSEFLAGS_HAS_WIN386) && (mouseflags & MOUSEFLAGS_VBOX_ENABLED)) { + if (flags.haswin386) { cli(); hook_int2f(&prev_int2f_handler, int2f_handler); sti(); - dlog_puts("int2F hooked!\n"); - mouseflags |= MOUSEFLAGS_INT2F_HOOKED; + flags.int2f_hooked = true; } #endif } @@ -233,23 +262,19 @@ VOID FAR PASCAL Enable(LPFN_MOUSEEVENT lpEventProc) /** Called by Windows to disable the mouse driver. */ VOID FAR PASCAL Disable(VOID) { - if (mouseflags & MOUSEFLAGS_ENABLED) { - dlog_puts("w16mouse: disable"); - + if (flags.enabled) { #if HOOK_INT2F - if (mouseflags & MOUSEFLAGS_INT2F_HOOKED) { + if (flags.int2f_hooked) { cli(); unhook_int2f(prev_int2f_handler); sti(); - dlog_puts("int2F unhooked!\n"); - mouseflags &= ~MOUSEFLAGS_INT2F_HOOKED; + flags.int2f_hooked = false; } #endif int33_reset(); - dlog_puts("w16mouse: int33 reset"); - mouseflags &= ~MOUSEFLAGS_ENABLED; + flags.enabled = false; } } -- cgit v1.2.3