aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJavier <dev.git@javispedro.com>2022-04-02 01:14:57 +0200
committerJavier <dev.git@javispedro.com>2022-04-02 01:14:57 +0200
commit3e39df4a4185f947d1af564aca265c0f6b51c9ec (patch)
tree453b4c1cb56cf029ebe96a02f91075912e64d0eb
parenta816d1a09b1045fb5c155ac73f3231fcf9d93180 (diff)
downloadvbados-3e39df4a4185f947d1af564aca265c0f6b51c9ec.tar.gz
vbados-3e39df4a4185f947d1af564aca265c0f6b51c9ec.zip
implement graphic cursor for CGA modes, wheel mouse detection, int2f hooking, simplify w16 driver
-rw-r--r--dlog.h82
-rw-r--r--dosmain.c66
-rw-r--r--dostsr.c526
-rw-r--r--dostsr.h56
-rw-r--r--int10vga.h31
-rw-r--r--int2fwin.h17
-rw-r--r--int33.h10
-rw-r--r--makefile14
-rw-r--r--ps2.h73
-rw-r--r--utils.h21
-rw-r--r--vbox.h51
-rw-r--r--w16mouse.c99
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 <ON|OFF> enable/disable virtualbox integration\n"
"\thostcur <ON|OFF> 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 <stdint.h>
#include <dos.h>
+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 <windows.h>
-#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;
}
}