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