diff options
| author | Javier <dev.git@javispedro.com> | 2022-04-02 01:14:57 +0200 | 
|---|---|---|
| committer | Javier <dev.git@javispedro.com> | 2022-04-02 01:14:57 +0200 | 
| commit | 3e39df4a4185f947d1af564aca265c0f6b51c9ec (patch) | |
| tree | 453b4c1cb56cf029ebe96a02f91075912e64d0eb | |
| parent | a816d1a09b1045fb5c155ac73f3231fcf9d93180 (diff) | |
| download | vbados-3e39df4a4185f947d1af564aca265c0f6b51c9ec.tar.gz vbados-3e39df4a4185f947d1af564aca265c0f6b51c9ec.zip  | |
implement graphic cursor for CGA modes, wheel mouse detection, int2f hooking, simplify w16 driver
| -rw-r--r-- | dlog.h | 82 | ||||
| -rw-r--r-- | dosmain.c | 66 | ||||
| -rw-r--r-- | dostsr.c | 526 | ||||
| -rw-r--r-- | dostsr.h | 56 | ||||
| -rw-r--r-- | int10vga.h | 31 | ||||
| -rw-r--r-- | int2fwin.h | 17 | ||||
| -rw-r--r-- | int33.h | 10 | ||||
| -rw-r--r-- | makefile | 14 | ||||
| -rw-r--r-- | ps2.h | 73 | ||||
| -rw-r--r-- | utils.h | 21 | ||||
| -rw-r--r-- | vbox.h | 51 | ||||
| -rw-r--r-- | w16mouse.c | 99 | 
12 files changed, 784 insertions, 262 deletions
@@ -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 */ @@ -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(); @@ -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" \ @@ -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; @@ -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 @@ -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  }; @@ -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] @@ -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) } @@ -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 @@ -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 = \ @@ -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 @@ -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;  	}  }  | 
