diff options
| -rw-r--r-- | README.md | 5 | ||||
| -rw-r--r-- | bda.h | 69 | ||||
| -rw-r--r-- | dlog.h | 58 | ||||
| -rw-r--r-- | doc/absapi.txt | 22 | ||||
| -rw-r--r-- | doc/int33.lst | 732 | ||||
| -rw-r--r-- | doc/wheelapi.txt | 109 | ||||
| -rw-r--r-- | doc/whlapi2.txt | 69 | ||||
| -rw-r--r-- | int10vga.h | 29 | ||||
| -rw-r--r-- | int15ps2.h | 90 | ||||
| -rw-r--r-- | int16kbd.h | 4 | ||||
| -rw-r--r-- | int33.h | 79 | ||||
| -rw-r--r-- | kitten.c | 786 | ||||
| -rw-r--r-- | kitten.h | 42 | ||||
| -rw-r--r-- | kittenc.c | 548 | ||||
| -rw-r--r-- | kittenc.h | 44 | ||||
| -rw-r--r-- | makefile | 51 | ||||
| -rw-r--r-- | mousetsr.c | 625 | ||||
| -rw-r--r-- | mousetsr.h | 90 | ||||
| -rw-r--r-- | mousew16.c | 165 | ||||
| -rw-r--r-- | mousew16.lnk | 6 | ||||
| -rw-r--r-- | mousmain.c | 527 | ||||
| -rw-r--r-- | moustest.c | 814 | ||||
| -rw-r--r-- | nls/.gitattributes | 2 | ||||
| -rw-r--r-- | nls/vbmouse.en | 26 | ||||
| -rw-r--r-- | nls/vbmouse.es | 26 | ||||
| -rw-r--r-- | nls/vbmouse.fr | 61 | ||||
| -rw-r--r-- | nls/vbmouse.tr | 61 | ||||
| -rw-r--r-- | nls/vbsf.fr | 60 | ||||
| -rw-r--r-- | nls/vbsf.tr | 60 | ||||
| -rw-r--r-- | pic.h | 59 | ||||
| -rw-r--r-- | serial.h | 291 | ||||
| -rw-r--r-- | sermouse.h | 50 | ||||
| -rw-r--r-- | sfmain.c | 28 | ||||
| -rw-r--r-- | unitbl/cp437uni.tbl (renamed from nls/cp437uni.tbl) | bin | 282 -> 282 bytes | |||
| -rw-r--r-- | unitbl/cp720uni.tbl (renamed from nls/cp720uni.tbl) | bin | 276 -> 276 bytes | |||
| -rw-r--r-- | unitbl/cp737uni.tbl (renamed from nls/cp737uni.tbl) | bin | 274 -> 274 bytes | |||
| -rw-r--r-- | unitbl/cp775uni.tbl (renamed from nls/cp775uni.tbl) | bin | 276 -> 276 bytes | |||
| -rw-r--r-- | unitbl/cp850uni.tbl (renamed from nls/cp850uni.tbl) | bin | 281 -> 281 bytes | |||
| -rw-r--r-- | unitbl/cp852uni.tbl (renamed from nls/cp852uni.tbl) | bin | 283 -> 283 bytes | |||
| -rw-r--r-- | unitbl/cp855uni.tbl (renamed from nls/cp855uni.tbl) | bin | 277 -> 277 bytes | |||
| -rw-r--r-- | unitbl/cp857uni.tbl (renamed from nls/cp857uni.tbl) | bin | 276 -> 276 bytes | |||
| -rw-r--r-- | unitbl/cp858uni.tbl (renamed from nls/cp858uni.tbl) | bin | 281 -> 281 bytes | |||
| -rw-r--r-- | unitbl/cp861uni.tbl (renamed from nls/cp861uni.tbl) | bin | 278 -> 278 bytes | |||
| -rw-r--r-- | unitbl/cp862uni.tbl (renamed from nls/cp862uni.tbl) | bin | 275 -> 275 bytes | |||
| -rw-r--r-- | unitbl/cp863uni.tbl (renamed from nls/cp863uni.tbl) | bin | 276 -> 276 bytes | |||
| -rw-r--r-- | unitbl/cp864uni.tbl (renamed from nls/cp864uni.tbl) | bin | 275 -> 275 bytes | |||
| -rw-r--r-- | unitbl/cp865uni.tbl (renamed from nls/cp865uni.tbl) | bin | 275 -> 275 bytes | |||
| -rw-r--r-- | unitbl/cp866uni.tbl (renamed from nls/cp866uni.tbl) | bin | 276 -> 276 bytes | |||
| -rw-r--r-- | unitbl/cp869uni.tbl (renamed from nls/cp869uni.tbl) | bin | 275 -> 275 bytes | |||
| -rw-r--r-- | unitbl/cp874uni.tbl (renamed from nls/cp874uni.tbl) | bin | 273 -> 273 bytes | |||
| -rw-r--r-- | unitbl/license.txt (renamed from nls/license.txt) | 0 | ||||
| -rw-r--r-- | unitbl2c.c | 206 | ||||
| -rw-r--r-- | utils.h | 7 | ||||
| -rw-r--r-- | version.h | 2 |
54 files changed, 4810 insertions, 1093 deletions
@@ -603,8 +603,8 @@ this shouldn't be a problem either. ## Future work -* The VirtualBox BIOS can crash on warm-boot (e.g. Ctrl+Alt+Del) if the mouse - was in the middle of sending a packet. A VM reboot fixes it. +* BIOS can crash on warm-boot (e.g. Ctrl+Alt+Del) if the mouse + was in the middle of sending a packet. A hardware reboot fixes it. We probably need to hook Ctrl+Alt+Del and turn off the mouse. * DOS has functions to start (FindFirst) and continue (FindNext) a directory @@ -626,4 +626,3 @@ this shouldn't be a problem either. * Would it be possible to use a hardware rendered mouse pointer in Windows 3.x, without having to replace the video driver? - This would also help other emulators. @@ -0,0 +1,69 @@ +/* + * VBMouse - BIOS data area access 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 BDA_H +#define BDA_H + +#include "utils.h" + +#define BIOS_DATA_AREA_SEGMENT 0x40 + +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; +} + +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_ebda_segment() bda_get_word(0x0e) + +#define bda_get_equipment() bda_get_word(0x10) + +#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_cur_video_page() bda_get_word(0x62) +#define bda_get_last_row() bda_get_byte(0x84) +#define bda_get_char_height() bda_get_word(0x85) + +#define bda_get_tick_count() bda_get_dword(0x6c) +#define bda_get_tick_count_lo() bda_get_word(0x6c) + +static inline void bda_wait_tick() { + uint16_t cur_ticks = bda_get_tick_count_lo(); + do { + pause(); + } while (cur_ticks == bda_get_tick_count_lo()); +} + +static inline void bda_wait_ticks(unsigned int ticks) { + for (; ticks > 0; ticks--) { + bda_wait_tick(); + } +} + +#endif // BDA_H @@ -29,14 +29,18 @@ /** If 0, these routines become nops */ #define ENABLE_DLOG 0 /** 1 means target serial port, 0 means target IO port. */ -#define DLOG_TARGET_SERIAL 0 -/** IO port to target. +#define DLOG_TARGET DLOG_TARGET_IOPORT +/** When using DLOG_TARGET_IOPORT, which IO port to target. * VirtualBox uses 0x504, Bochs, DOSBox and descendants use 0xE9. - * When using DLOG_TARGET_SERIAL, use desired UART IO base port. (e.g. COM1 = 0x3F8). */ + * When using DLOG_TARGET_SERIAL, the desired UART IO base port. (e.g. COM1 = 0x3F8). */ #define DLOG_TARGET_PORT 0x504 // End of customizable defines +#define DLOG_TARGET_BIOSTTY 1 +#define DLOG_TARGET_SERIAL 2 +#define DLOG_TARGET_IOPORT 3 + #if ENABLE_DLOG /** Initializes the debug log port. */ @@ -45,7 +49,34 @@ static void dlog_init(); /** Logs a single character to the debug message IO port. */ static inline void dputc(char c); -#if DLOG_TARGET_SERIAL +#if DLOG_TARGET == DLOG_TARGET_BIOSTTY + +static inline void dlog_init() +{ + // No initialization required +} + +static inline void dconputc(char c); +#pragma aux dconputc = \ + "mov ah, 0x0E" \ + "xor bx, bx" \ + "int 0x10" \ + __parm [AL] \ + __modify __exact [AH AL BH BL]; + +static inline void dputc(char c) +{ + // Need to convert CR into CRLF... + if (c == '\n') { + dconputc('\n'); + dconputc('\r'); + } else { + dconputc(c); + } +} + + +#elif DLOG_TARGET == DLOG_TARGET_SERIAL static void dlog_init() { @@ -65,7 +96,7 @@ static inline void dputc(char c) outp(DLOG_TARGET_PORT, c); } -#else /* DLOG_TARGET_SERIAL */ +#elif DLOG_TARGET == DLOG_TARGET_IOPORT static inline void dlog_init() { @@ -77,7 +108,7 @@ static inline void dputc(char c) outp(DLOG_TARGET_PORT, c); } -#endif /* DLOG_TARGET_SERIAL */ +#endif /* DLOG_TARGET */ static void d_utoa(unsigned num, unsigned base) { @@ -148,7 +179,6 @@ static void d_itoa(int num, unsigned base) { unsigned unum; - // TODO if (num < 0) { dputc('-'); unum = -num; @@ -163,7 +193,6 @@ static void d_ltoa(long num, unsigned base) { unsigned long unum; - // TODO if (num < 0) { dputc('-'); unum = -num; @@ -265,6 +294,16 @@ static void dprintf(const char *fmt, ...) break; } break; + case 'b': + switch (size) { + case sizeof(int): + d_utoa(va_arg(va, int), 2); + break; + case sizeof(long): + d_ultoa(va_arg(va, long), 2); + break; + } + break; case 'p': if (is_far) { void __far *p = va_arg(va, void __far *); @@ -280,6 +319,9 @@ static void dprintf(const char *fmt, ...) : va_arg(va, const char *), precision); break; + case 'c': + dputc(va_arg(va, char)); + break; case '%': dputc('%'); break; diff --git a/doc/absapi.txt b/doc/absapi.txt new file mode 100644 index 0000000..ffb29f8 --- /dev/null +++ b/doc/absapi.txt @@ -0,0 +1,22 @@ +This document describes an extension to the int33 API to allow users to detect +if mouse interrupts come from a relative-input device (like a standard mouse) +or an absolute-input device (like a tablet, or an emulator like VirtualBox). + +--------------------------------------------------------------------- +Changes in the original INT 33h functions: + +INT 33/000C - Define User Interrupt Routine +INT 33/0014 - Exchange User Interrupt Routines +Bitfields for mouse call mask: +Bit(s) Description + 0-6 same as Table 03171 (as in int33.lst) + 7 (for vertical wheel movement -- seee wheelapi.txt) + 8 absolute mouse event + 9-15 unused +Notes: when the user interrupt routine is called, bit 8 of CX indicates that + the x, y coordinates passed in CX, DX come from an absolute pointing + device (and therefore that the mickey counts in SI, DI may be zero or + virtualized). + bit 8 will not be set unless the user also sets bits 8 in the event mask + passed to int33/000c or int33/0014. However, setting or clearing bit 8 + in the event mask shall have no other effect. diff --git a/doc/int33.lst b/doc/int33.lst new file mode 100644 index 0000000..dcf9ad0 --- /dev/null +++ b/doc/int33.lst @@ -0,0 +1,732 @@ +--------M-330000-----------------------------
+INT 33 - MS MOUSE - RESET DRIVER AND READ STATUS
+ AX = 0000h
+Return: AX = status
+ 0000h hardware/driver not installed
+ FFFFh hardware/driver installed
+ BX = number of buttons
+ 0000h other than two
+ 0002h two buttons (many drivers)
+ 0003h Mouse Systems/Logitech three-button mouse
+ FFFFh two buttons
+Notes: since INT 33 might be uninitialized on old machines, the caller
+ should first check that INT 33 is neither 0000h:0000h nor points at
+ an IRET instruction (BYTE CFh) before calling this API
+ to use mouse on a Hercules-compatible monographics card in graphics
+ mode, you must first set 0040h:0049h to 6 for page 0 or 5 for page 1,
+ and then call this function. Logitech drivers v5.01 and v6.00
+ reportedly do not correctly use Hercules graphics in dual-monitor
+ systems, while version 4.10 does.
+ the Logitech mouse driver contains the signature string "LOGITECH"
+ three bytes past the interrupt handler; many of the Logitech mouse
+ utilities check for this signature.
+ Logitech MouseWare v6.30 reportedly does not support CGA video modes
+ if no CGA is present when it is started and the video board is
+ later switched into CGA emulation
+SeeAlso: AX=0011h,AX=0021h,AX=002Fh,INT 62/AX=007Ah,INT 74
+--------M-330001-----------------------------
+INT 33 - MS MOUSE v1.0+ - SHOW MOUSE CURSOR
+ AX = 0001h
+SeeAlso: AX=0002h,INT 16/AX=FFFEh,INT 62/AX=007Bh,INT 6F/AH=06h"F_TRACK_ON"
+--------M-330002-----------------------------
+INT 33 - MS MOUSE v1.0+ - HIDE MOUSE CURSOR
+ AX = 0002h
+Note: multiple calls to hide the cursor will require multiple calls to
+ function 01h to unhide it.
+SeeAlso: AX=0001h,AX=0010h,INT 16/AX=FFFFh,INT 62/AX=007Bh
+SeeAlso: INT 6F/AH=08h"F_TRACK_OFF"
+--------M-330003-----------------------------
+INT 33 - MS MOUSE v1.0+ - RETURN POSITION AND BUTTON STATUS
+ AX = 0003h
+Return: BX = button status (see #03168)
+ CX = column
+ DX = row
+Note: in text modes, all coordinates are specified as multiples of the cell
+ size, typically 8x8 pixels
+SeeAlso: AX=0004h,AX=000Bh,INT 2F/AX=D000h"ZWmous"
+
+Bitfields for mouse button status:
+Bit(s) Description (Table 03168)
+ 0 left button pressed if 1
+ 1 right button pressed if 1
+ 2 middle button pressed if 1 (Mouse Systems/Logitech/Genius)
+--------M-330004-----------------------------
+INT 33 - MS MOUSE v1.0+ - POSITION MOUSE CURSOR
+ AX = 0004h
+ CX = column
+ DX = row
+Note: the row and column are truncated to the next lower multiple of the cell
+ size (typically 8x8 in text modes); however, some versions of the
+ Microsoft documentation incorrectly state that the coordinates are
+ rounded
+SeeAlso: AX=0003h,INT 62/AX=0081h,INT 6F/AH=10h"F_PUT_SPRITE"
+--------M-330005-----------------------------
+INT 33 - MS MOUSE v1.0+ - RETURN BUTTON PRESS DATA
+ AX = 0005h
+ BX = button number (see #03169)
+Return: AX = button states (see #03168)
+ BX = number of times specified button has been pressed since last call
+ CX = column at time specified button was last pressed
+ DX = row at time specified button was last pressed
+Note: at least for the Genius mouse driver, the number of button presses
+ returned is limited to 7FFFh
+SeeAlso: AX=0006h,INT 62/AX=007Ch
+
+(Table 03169)
+Values for mouse button number:
+ 0000h left
+ 0001h right
+ 0002h middle (Mouse Systems/Logitech/Genius mouse)
+--------M-330006-----------------------------
+INT 33 - MS MOUSE v1.0+ - RETURN BUTTON RELEASE DATA
+ AX = 0006h
+ BX = button number (see #03169)
+Return: AX = button states (see #03168)
+ BX = number of times specified button has been released since last call
+ CX = column at time specified button was last released
+ DX = row at time specified button was last released
+Note: at least for the Genius mouse driver, the number of button releases
+ returned is limited to 7FFFh
+SeeAlso: AX=0005h,INT 62/AX=007Ch
+--------M-330007-----------------------------
+INT 33 - MS MOUSE v1.0+ - DEFINE HORIZONTAL CURSOR RANGE
+ AX = 0007h
+ CX = minimum column
+ DX = maximum column
+Note: in text modes, the minimum and maximum columns are truncated to the
+ next lower multiple of the cell size, typically 8x8 pixels
+SeeAlso: AX=0008h,AX=0010h,AX=0031h,INT 62/AX=0080h
+SeeAlso: INT 6F/AH=0Ch"F_SET_LIMITS_X"
+--------M-330008-----------------------------
+INT 33 - MS MOUSE v1.0+ - DEFINE VERTICAL CURSOR RANGE
+ AX = 0008h
+ CX = minimum row
+ DX = maximum row
+Note: in text modes, the minimum and maximum rows are truncated to the
+ next lower multiple of the cell size, typically 8x8 pixels
+SeeAlso: AX=0007h,AX=0010h,AX=0031h,INT 62/AX=0080h
+SeeAlso: INT 6F/AH=0Eh"F_SET_LIMITS_Y"
+--------M-330009-----------------------------
+INT 33 - MS MOUSE v3.0+ - DEFINE GRAPHICS CURSOR
+ AX = 0009h
+ BX = column of cursor hot spot in bitmap (-16 to 16)
+ CX = row of cursor hot spot (-16 to 16)
+ ES:DX -> mask bitmap (see #03170)
+Notes: in graphics modes, the screen contents around the current mouse cursor
+ position are ANDed with the screen mask and then XORed with the
+ cursor mask
+ the Microsoft mouse driver v7.04 and v8.20 uses only BL and CL, so the
+ hot spot row/column should be limited to -128..127
+ Microsoft KnowledgeBase article Q19850 states that the high bit is
+ right-most, but that statement is contradicted by all other available
+ documentation
+SeeAlso: AX=000Ah,AX=0012h,AX=002Ah,INT 62/AX=007Fh,INT 6F/AH=0Ah"F_DEF_MASKS"
+
+Format of mouse mask bitmap:
+Offset Size Description (Table 03170)
+ 00h 16 WORDs screen mask
+ 10h 16 WORDs cursor mask
+Note: each word defines the sixteen pixels of a row, low bit rightmost
+--------M-33000A-----------------------------
+INT 33 - MS MOUSE v3.0+ - DEFINE TEXT CURSOR
+ AX = 000Ah
+ BX = hardware/software text cursor
+ 0000h software
+ CX = screen mask
+ DX = cursor mask
+ 0001h hardware
+ CX = start scan line
+ DX = end scan line
+Note: when the software cursor is selected, the character/attribute data at
+ the current screen position is ANDed with the screen mask and then
+ XORed with the cursor mask
+SeeAlso: AX=0009h,INT 62/AX=007Eh
+--------M-33000B-----------------------------
+INT 33 - MS MOUSE v1.0+ - READ MOTION COUNTERS
+ AX = 000Bh
+Return: CX = number of mickeys mouse moved horizontally since last call
+ DX = number of mickeys mouse moved vertically
+Notes: a mickey is the smallest increment the mouse can sense
+ positive values indicate down/right
+SeeAlso: AX=0003h,AX=001Bh,AX=0027h
+--------M-33000C-----------------------------
+INT 33 - MS MOUSE v1.0+ - DEFINE INTERRUPT SUBROUTINE PARAMETERS
+ AX = 000Ch
+ CX = call mask (see #03171)
+ ES:DX -> FAR routine (see #03172)
+SeeAlso: AX=0018h
+
+Bitfields for mouse call mask:
+Bit(s) Description (Table 03171)
+ 0 call if mouse moves
+ 1 call if left button pressed
+ 2 call if left button released
+ 3 call if right button pressed
+ 4 call if right button released
+ 5 call if middle button pressed (Mouse Systems/Logitech/Genius mouse)
+ 6 call if middle button released (Mouse Systems/Logitech/Genius mouse)
+ 7-15 unused
+Note: some versions of the Microsoft documentation incorrectly state that CX
+ bit 0 means call if mouse cursor moves
+
+(Table 03172)
+Values interrupt routine is called with:
+ AX = condition mask (same bit assignments as call mask)
+ BX = button state
+ CX = cursor column
+ DX = cursor row
+ SI = horizontal mickey count
+ DI = vertical mickey count
+Notes: some versions of the Microsoft documentation erroneously swap the
+ meanings of SI and DI
+ in text modes, the row and column will be reported as a multiple of
+ the character cell size, typically 8x8 pixels
+--------M-33000D-----------------------------
+INT 33 - MS MOUSE v1.0+ - LIGHT PEN EMULATION ON
+ AX = 000Dh
+SeeAlso: AX=000Eh,INT 10/AH=04h
+--------M-33000E-----------------------------
+INT 33 - MS MOUSE v1.0+ - LIGHT PEN EMULATION OFF
+ AX = 000Eh
+SeeAlso: AX=000Dh
+--------M-33000F-----------------------------
+INT 33 - MS MOUSE v1.0+ - DEFINE MICKEY/PIXEL RATIO
+ AX = 000Fh
+ CX = number of mickeys per 8 pixels horizontally (default 8)
+ DX = number of mickeys per 8 pixels vertically (default 16)
+SeeAlso: AX=0013h,AX=001Ah,INT 62/AX=0082h
+--------M-330010-----------------------------
+INT 33 - MS MOUSE v1.0+ - DEFINE SCREEN REGION FOR UPDATING
+ AX = 0010h
+ CX,DX = X,Y coordinates of upper left corner
+ SI,DI = X,Y coordinates of lower right corner
+Note: mouse cursor is hidden in the specified region, and needs to be
+ explicitly turned on again
+SeeAlso: AX=0001h,AX=0002h,AX=0007h,AX=0010h"Genius MOUSE",AX=0031h
+--------M-330012-----------------------------
+INT 33 - MS MOUSE - SET LARGE GRAPHICS CURSOR BLOCK
+ AX = 0012h
+ BH = cursor width in words
+ CH = rows in cursor
+ BL = horizontal hot spot (-16 to 16)
+ CL = vertical hot spot (-16 to 16)
+ ES:DX -> bit map of screen and cursor maps
+Return: AX = FFFFh if successful
+SeeAlso: AX=0009h,AX=002Ah,AX=0035h
+--------M-330013-----------------------------
+INT 33 - MS MOUSE v5.0+ - DEFINE DOUBLE-SPEED THRESHOLD
+ AX = 0013h
+ DX = threshold speed in mickeys/second, 0000h = default of 64/second
+Note: if speed exceeds threshold, the cursor's on-screen motion is doubled
+SeeAlso: AX=000Fh,AX=001Bh,AX=002Ch
+--------M-330014-----------------------------
+INT 33 - MS MOUSE v3.0+ - EXCHANGE INTERRUPT SUBROUTINES
+ AX = 0014h
+ CX = call mask (see #03171)
+ ES:DX -> FAR routine
+Return: CX = call mask of previous interrupt routine
+ ES:DX = FAR address of previous interrupt routine
+SeeAlso: AX=000Ch,AX=0018h
+--------M-330015-----------------------------
+INT 33 - MS MOUSE v6.0+ - RETURN DRIVER STORAGE REQUIREMENTS
+ AX = 0015h
+Return: BX = size of buffer needed to store driver state
+SeeAlso: AX=0016h,AX=0017h,AX=0042h
+--------M-330016-----------------------------
+INT 33 - MS MOUSE v6.0+ - SAVE DRIVER STATE
+ AX = 0016h
+ BX = size of buffer (see AX=0015h)
+ ES:DX -> buffer for driver state
+Note: although not documented (since the Microsoft driver does not use it),
+ many drivers appear to require BX on input
+SeeAlso: AX=0015h,AX=0017h
+--------M-330017-----------------------------
+INT 33 - MS MOUSE v6.0+ - RESTORE DRIVER STATE
+ AX = 0017h
+ BX = size of buffer (see AX=0015h)
+ ES:DX -> buffer containing saved state
+Notes: although not documented (since the Microsoft driver does not use it),
+ many drivers appear to require BX on input
+ some mouse drivers range-check the values in the saved state based on
+ the current video mode; thus, the video mode should be restored
+ before the mouse driver's state is restored
+SeeAlso: AX=0015h,AX=0016h
+--------M-330018-----------------------------
+INT 33 - MS MOUSE v6.0+ - SET ALTERNATE MOUSE USER HANDLER
+ AX = 0018h
+ CX = call mask (see #03174)
+ ES:DX -> FAR routine to be invoked on mouse events (see #03175)
+Return: AX = status
+ 0018h if successful
+ FFFFh on error
+Notes: up to three handlers can be defined by separate calls to this function,
+ each with a different combination of shift states in the call mask;
+ calling this function again with a call mask of 0000h undefines the
+ specified handler (official documentation); specifying the same
+ call mask and an address of 0000h:0000h undefines the handler (real
+ life)
+ some versions of the documentation erroneously reverse the order of
+ the bits in the call mask
+SeeAlso: AX=000Ch,AX=0014h,AX=0019h
+
+Bitfields for mouse call mask:
+Bit(s) Description (Table 03174)
+ 0 call if mouse moves
+ 1 call if left button pressed
+ 2 call if left button released
+ 3 call if right button pressed
+ 4 call if right button released
+ 5 call if shift button pressed during event
+ 6 call if ctrl key pressed during event
+ 7 call if alt key pressed during event
+Note: at least one of 5-7 must be set
+
+(Table 03175)
+Values user handler is called with:
+ AX = condition mask (same bit assignments as call mask)
+ BX = button state
+ CX = cursor column
+ DX = cursor row
+ SI = horizontal mickey count
+ DI = vertical mickey count
+Return: registers preserved
+Note: in text modes, the row and column will be reported as a multiple of
+ the cell size, typically 8x8 pixels
+--------M-330019-----------------------------
+INT 33 - MS MOUSE v6.0+ - RETURN USER ALTERNATE INTERRUPT VECTOR
+ AX = 0019h
+ CX = call mask (see #03174)
+Return: BX:DX = user interrupt vector
+ CX = call mask (0000h if not found)
+Note: attempts to find a user event handler (defined by function 18h)
+ whose call mask matches CX
+SeeAlso: AX=0018h
+--------M-33001A-----------------------------
+INT 33 - MS MOUSE v6.0+ - SET MOUSE SENSITIVITY
+ AX = 001Ah
+ BX = horizontal speed \
+ CX = vertical speed / (see AX=000Fh)
+ DX = double speed threshold (see AX=0013h)
+SeeAlso: AX=0013h,AX=001Bh,INT 62/AX=0082h
+--------M-33001B-----------------------------
+INT 33 - MS MOUSE v6.0+ - RETURN MOUSE SENSITIVITY
+ AX = 001Bh
+Return: BX = horizontal speed
+ CX = vertical speed
+ DX = double speed threshold
+SeeAlso: AX=000Bh,AX=001Ah
+--------M-33001C-----------------------------
+INT 33 - MS MOUSE v6.0+ - SET INTERRUPT RATE
+ AX = 001Ch
+ BX = rate (see #03176)
+Notes: only available on InPort mouse
+ values greater than 4 may cause unpredictable driver behavior
+
+(Table 03176)
+Values for mouse interrupt rate:
+ 00h no interrupts allowed
+ 01h 30 per second
+ 02h 50 per second
+ 03h 100 per second
+ 04h 200 per second
+--------M-33001D-----------------------------
+INT 33 - MS MOUSE v6.0+ - DEFINE DISPLAY PAGE NUMBER
+ AX = 001Dh
+ BX = display page number
+Note: the cursor will be displayed on the specified page
+SeeAlso: AX=001Eh
+--------M-33001E-----------------------------
+INT 33 - MS MOUSE v6.0+ - RETURN DISPLAY PAGE NUMBER
+ AX = 001Eh
+Return: BX = display page number
+SeeAlso: AX=001Dh
+--------M-33001F-----------------------------
+INT 33 - MS MOUSE v6.0+ - DISABLE MOUSE DRIVER
+ AX = 001Fh
+Return: AX = status
+ 001Fh successful
+ ES:BX = INT 33 vector before mouse driver was first installed
+ FFFFh unsuccessful
+Notes: restores vectors for INT 10 and INT 71 (8086) or INT 74 (286/386)
+ if you restore INT 33 to ES:BX, driver will be completely disabled
+ many drivers return AX=001Fh even though the driver has been disabled
+SeeAlso: AX=0020h
+--------M-330020-----------------------------
+INT 33 - MS MOUSE v6.0+ - ENABLE MOUSE DRIVER
+ AX = 0020h
+Return: AX = status
+ 0020h successful
+ FFFFh unsuccessful
+Notes: restores vectors for INT 10h and INT 71h (8086) or INT 74h (286/386)
+ which were removed by function 1Fh
+ Microsoft's documentation states that no value is returned
+SeeAlso: AX=001Fh
+--------M-330021-----------------------------
+INT 33 - MS MOUSE v6.0+ - SOFTWARE RESET
+ AX = 0021h
+Return: AX = status
+ FFFFh if mouse driver installed
+ BX = number of buttons (FFFFh = two buttons)
+ 0021h if mouse driver not installed
+Note: this call is identical to funtion 00h, but does not reset the mouse
+SeeAlso: AX=0000h
+--------M-330022-----------------------------
+INT 33 - MS MOUSE v6.0+ - SET LANGUAGE FOR MESSAGES
+ AX = 0022h
+ BX = language (see #03177)
+Note: only available on international versions of the driver; US versions
+ ignore this call
+SeeAlso: AX=0023h
+
+(Table 03177)
+Values for mouse driver language:
+ 00h English
+ 01h French
+ 02h Dutch
+ 03h German
+ 04h Swedish
+ 05h Finnish
+ 06h Spanish
+ 07h Portugese
+ 08h Italian
+--------M-330023-----------------------------
+INT 33 - MS MOUSE v6.0+ - GET LANGUAGE FOR MESSAGES
+ AX = 0023h
+Return: BX = language (see #03177)
+Note: the US version of the driver always returns zero
+SeeAlso: AX=0022h
+--------M-330024BX0000-----------------------
+INT 33 - MS MOUSE v6.26+ - GET SOFTWARE VERSION, MOUSE TYPE, AND IRQ NUMBER
+ AX = 0024h
+ BX = 0000h to check for function's existence
+Return: AX = FFFFh on error
+ otherwise,
+ BH = major version
+ BL = minor version
+ CH = type (1=bus, 2=serial, 3=InPort, 4=PS/2, 5=HP)
+ CL = interrupt (0=PS/2, 2=IRQ2, 3=IRQ3,...,7=IRQ7,...,0Fh=IRQ15)
+Note: although current Microsoft documentation states that this function was
+ introduced in v6.26, it appears to have been present as early as
+ v6.02 (for earlier versions, use INT 33/AX=006Dh)
+SeeAlso: AX=004Dh,AX=006Dh
+--------M-330025-----------------------------
+INT 33 - MS MOUSE v6.26+ - GET GENERAL DRIVER INFORMATION
+ AX = 0025h
+Return: AX = general information (see #03178)
+ BX = cursor lock flag for OS/2 to prevent reentrancy problems
+ CX = mouse code active flag (for OS/2)
+ DX = mouse driver busy flag (for OS/2)
+
+Bitfields for general mouse driver information:
+Bit(s) Description (Table 03178)
+ 15 driver loaded as device driver rather than TSR
+ 14 driver is newer integrated type
+ 13,12 current cursor type
+ 00 software text cursor
+ 01 hardware text cursor (CRT Controller's cursor)
+ 1X graphics cursor
+ 11-8 interrupt rate (see #03176)
+ 7-0 count of currently-active Mouse Display Drivers (MDD), the newer
+ integrated driver type
+--------M-330026-----------------------------
+INT 33 - MS MOUSE v6.26+ - GET MAXIMUM VIRTUAL COORDINATES
+ AX = 0026h
+Return: BX = mouse-disabled flag (0000h mouse enabled, nonzero disabled)
+ CX = maximum virtual X (for current video mode)
+ DX = maximum virtual Y
+Note: for driver versions before 7.05, this call returns the currently-set
+ maximum coordinates; v7.05+ returns the absolute maximum coordinates
+SeeAlso: AX=0031h
+--------M-330027-----------------------------
+INT 33 - MS MOUSE v7.01+ - GET SCREEN/CURSOR MASKS AND MICKEY COUNTS
+ AX = 0027h
+Return: AX = screen-mask value (or hardware cursor scan-line start for v7.02+)
+ BX = cursor-mask value (or hardware cursor scan-line stop for v7.02+)
+ CX = horizontal mickeys moved since last call
+ DX = vertical mickeys moved since last call
+SeeAlso: AX=000Bh
+--------M-330028-----------------------------
+INT 33 - MS MOUSE v7.0+ - SET VIDEO MODE
+ AX = 0028h
+ CX = new video mode (call is NOP if 0000h)
+ DH = Y font size (00h = default)
+ DL = X font size (00h = default)
+Return: CL = status (00h = successful)
+Notes: DX is ignored unless the selected video mode supports font size control
+ when CX=0000h, an internal flag that had been set by a previous call
+ is cleared; this is required before a mouse reset
+SeeAlso: AX=0029h,INT 10/AH=00h
+--------M-330029-----------------------------
+INT 33 - MS MOUSE v7.0+ - ENUMERATE VIDEO MODES
+ AX = 0029h
+ CX = previous video mode
+ 0000h get first supported video mode
+ other get next supported mode after mode CX
+Return: CX = first/next video mode (0000h = no more video modes)
+ DS:DX -> description of video mode or 0000h:0000h if none
+Notes: the enumerated video modes may be in any order and may repeat
+ the description string (if available) is terminated by '$' followed by
+ a NUL byte
+SeeAlso: AX=0028h
+--------M-33002A-----------------------------
+INT 33 - MS MOUSE v7.02+ - GET CURSOR HOT SPOT
+ AX = 002Ah
+Return: AX = internal counter controlling cursor visibility
+ BX = cursor hot spot column
+ CX = cursor hot spot row
+ DX = mouse type (see #03179)
+Note: the hot spot location is relative to the upper left corner of the
+ cursor block and may range from -128 to +127 both horizontally and
+ vertically
+SeeAlso: AX=0009h,AX=0012h,AX=0035h
+
+(Table 03179)
+Values for mouse type:
+ 00h none
+ 01h bus
+ 02h serial
+ 03h InPort
+ 04h IBM
+ 05h Hewlett-Packard
+--------M-33002B-----------------------------
+INT 33 - MS MOUSE v7.0+ - LOAD ACCELERATION PROFILES
+ AX = 002Bh
+ BX = active acceleration profile
+ 0001h-0004h or FFFFh to restore default curves
+ ES:SI -> buffer containing acceleration profile data (see #03180)
+Return: AX = success flag
+SeeAlso: AX=002Ch,AX=002Dh,AX=0033h
+
+Format of acceleration profile data:
+Offset Size Description (Table 03180)
+ 00h BYTE length of acceleration profile 1
+ 01h BYTE length of acceleration profile 2
+ 02h BYTE length of acceleration profile 3
+ 03h BYTE length of acceleration profile 4
+ 04h 32 BYTEs threshold speeds for acceleration profile 1
+ 24h 32 BYTEs threshold speeds for acceleration profile 2
+ 44h 32 BYTEs threshold speeds for acceleration profile 3
+ 64h 32 BYTEs threshold speeds for acceleration profile 4
+ 84h 32 BYTEs speedup factor for acceleration profile 1
+ (10h = 1.0, 14h = 1.25, 20h = 2.0, etc)
+ A4h 32 BYTEs speedup factor for acceleration profile 2
+ (10h = 1.0, 14h = 1.25, 20h = 2.0, etc)
+ C4h 32 BYTEs speedup factor for acceleration profile 3
+ (10h = 1.0, 14h = 1.25, 20h = 2.0, etc)
+ E4h 32 BYTEs speedup factor for acceleration profile 4
+ (10h = 1.0, 14h = 1.25, 20h = 2.0, etc)
+104h 16 BYTEs name of acceleration profile 1 (blank-padded)
+114h 16 BYTEs name of acceleration profile 2 (blank-padded)
+124h 16 BYTEs name of acceleration profile 3 (blank-padded)
+134h 16 BYTEs name of acceleration profile 4 (blank-padded)
+Note: unused bytes in the threshold speed fields are filled with 7Fh and
+ unused bytes in the speedup factor fields are filled with 10h
+--------M-33002C-----------------------------
+INT 33 - MS MOUSE v7.0+ - GET ACCELERATION PROFILES
+ AX = 002Ch
+Return: AX = status (0000h success)
+ BX = currently-active acceleration profile
+ ES:SI -> acceleration profile data (see #03180)
+SeeAlso: AX=002Bh,AX=002Dh,AX=0033h
+--------M-33002D-----------------------------
+INT 33 - MS MOUSE v7.0+ - SELECT ACCELERATION PROFILE
+ AX = 002Dh
+ BX = acceleration level
+ 0001h-0004h to set profile, or FFFFh to get current profile
+Return: AX = status
+ 0000h successful
+ ES:SI -> 16-byte blank-padded name of acceleration profile
+ FFFEh invalid acceleration curve number
+ ES:SI destroyed
+ BX = active acceleration curve number
+SeeAlso: AX=0013h,AX=002Bh,AX=002Ch,AX=002Eh
+--------M-33002E-----------------------------
+INT 33 - MS MOUSE v8.10+ - SET ACCELERATION PROFILE NAMES
+ AX = 002Eh
+ BL = flag (if nonzero, fill ES:SI buffer with default names on return)
+ ES:SI -> 64-byte buffer containing profile names (16 bytes per name)
+Return: AX = status (0000h success)
+ FFFEh error for ATI Mouse driver
+ ES:SI buffer filled with default names if BL nonzero on entry
+Notes: not supported by Logitech driver v6.10
+ supported by ATI Mouse driver v7.04
+SeeAlso: AX=002Ch,AX=002Dh,AX=012Eh,AX=022Eh
+--------M-33002F-----------------------------
+INT 33 - MS MOUSE v7.02+ - MOUSE HARDWARE RESET
+ AX = 002Fh
+Return: AX = status
+Note: invoked by mouse driver v8.20 on being called with INT 2F/AX=530Bh
+SeeAlso: INT 2F/AH=53h
+--------M-330030-----------------------------
+INT 33 - MS MOUSE v7.04+ - GET/SET BallPoint INFORMATION
+ AX = 0030h
+ CX = command
+ 0000h get status of BallPoint device
+ other set rotation angle and masks
+ BX = rotation angle (-32768 to 32767 degrees)
+ CH = primary button mask
+ CL = secondary button mask
+Return: AX = button status (FFFFh if no BallPoint) (see #03181)
+ BX = rotation angle (0-360 degrees)
+ CH = primary button mask
+ CL = secondary button mask
+Note: not supported by the ATI Mouse driver which calls itself v7.04
+
+Bitfields for BallPoint mouse button status:
+Bit(s) Description (Table 03181)
+ 5 button 1
+ 4 button 2
+ 3 button 3
+ 2 button 4
+ other zero
+--------M-330031-----------------------------
+INT 33 - MS MOUSE v7.05+ - GET CURRENT MINIMUM/MAXIMUM VIRTUAL COORDINATES
+ AX = 0031h
+Return: AX = virtual X minimum
+ BX = virtual Y minimum
+ CX = virtual X maximum
+ DX = virtual Y maximum
+Note: the minimum and maximum values are those set by AX=0007h and AX=0008h;
+ the default is minimum = 0 and maximum = absolute maximum
+ (see AX=0026h)
+SeeAlso: AX=0007h,AX=0008h,AX=0010h,AX=0026h
+--------M-330032-----------------------------
+INT 33 - MS MOUSE v7.05+ - GET ACTIVE ADVANCED FUNCTIONS
+ AX = 0032h
+Return: AX = active function flags (FFFFh for v8.10)
+ bit 15: function 0025h supported
+ bit 14: function 0026h supported
+ ...
+ bit 0: function 0034h supported
+ BX = ??? (0000h) officially unused
+ CX = ??? (E000h) officially unused
+ DX = ??? (0000h) officially unused
+Note: the Italian version of MS MOUSE v8.20 reportedly indicates that
+ functions 0033h and 0034h are not supported even though they are
+--------M-330033-----------------------------
+INT 33 - MS MOUSE v7.05+ - GET SWITCH SETTINGS AND ACCELERATION PROFILE DATA
+ AX = 0033h
+ CX = size of buffer
+ 0000h get required buffer size
+ Return: AX = 0000h
+ CX = required size (0154h for Logitech v6.10, 0159h
+ for MS v8.10-8.20)
+ other
+ ES:DX -> buffer of CX bytes for mouse settings
+ Return: AX = 0000h
+ CX = number of bytes returned
+ ES:DX buffer filled (see #03182)
+SeeAlso: AX=002Bh
+
+Format of mouse settings data buffer:
+Offset Size Description (Table 03182)
+ 00h BYTE mouse type
+ 01h BYTE current language
+ 02h BYTE horizontal sensitivity (00h-64h)
+ 03h BYTE vertical sensitivity (00h-64h)
+ 04h BYTE double-speed threshold (00h-64h)
+ 05h BYTE ballistic curve (01h-04h)
+ 06h BYTE interrupt rate (01h-04h)
+ 07h BYTE cursor override mask
+ 08h BYTE laptop adjustment
+ 09h BYTE memory type (00h-02h)
+ 0Ah BYTE SuperVGA support (00h,01h)
+ 0Bh BYTE rotation angle
+ 0Ch BYTE ???
+ 0Dh BYTE primary button (01h-04h)
+ 0Eh BYTE secondary button (01h-04h)
+ 0Fh BYTE click lock enabled (00h,01h)
+ 10h 324 BYTEs acceleration profile data (see #03180)
+154h 5 BYTEs ??? (Microsoft driver, but not Logitech)
+--------M-330034-----------------------------
+INT 33 - MS MOUSE v8.0+ - GET INITIALIZATION FILE
+ AX = 0034h
+Return: AX = status (0000h successful)
+ ES:DX -> ASCIZ initialization (.INI) file name
+--------M-330035-----------------------------
+INT 33 - MS MOUSE v8.10+ - LCD SCREEN LARGE POINTER SUPPORT
+ AX = 0035h
+ BX = function
+ FFFFh get current settings
+ Return: AX = 0000h
+ BH = style (see #03183)
+ BL = size (see #03184)
+ CH = threshold (00h-64h)
+ CL = active flag (00h disabled, 01h enabled)
+ DX = delay
+ other
+ BH = pointer style (see #03183)
+ BL = size (see #03184)
+ CH = threshold (00h-64h)
+ CL = active flag (00h disable size change, 01h enable)
+ DX = delay (0000h-0064h)
+ Return: AX = 0000h
+Note: not supported by Logitech driver v6.10
+SeeAlso: AX=0012h,AX=002Ah
+
+(Table 03183)
+Values for pointer style:
+ 00h normal
+ 01h reverse
+ 02h transparent
+SeeAlso: #03184
+
+(Table 03184)
+Values for pointer size:
+ 00h small ("1")
+ 01h medium ("1.5")
+ 02h large ("2")
+SeeAlso: #03183
+--------M-33004D-----------------------------
+INT 33 - MS MOUSE - RETURN POINTER TO COPYRIGHT STRING
+ AX = 004Dh
+Return: ES:DI -> copyright message "*** This is Copyright 1983 Microsoft" or
+ "Copyright 19XX...."
+Notes: also supported by Logitech, Kraft, Genius Mouse, and Mouse Systems
+ mouse drivers
+ in the Genius Mouse 9.06 driver, the ASCIZ signature "KYE" immediately
+ follows the above copyright message (KYE Corp. manufactures the
+ driver)
+SeeAlso: AX=0024h,AX=006Dh,AX=0666h
+--------M-33006D-----------------------------
+INT 33 - MS MOUSE - GET VERSION STRING
+ AX = 006Dh 'm'
+Return: ES:DI -> Microsoft version number of resident driver (see #03187)
+Notes: also supported by Logitech, Mouse Systems, Kraft, and Genius mouse
+ drivers
+ the Mouse Systems 7.01 and Genius Mouse 9.06 drivers report their
+ Microsoft version as 7.00 even though they do not support any of the
+ functions from 0025h through 002Dh supported by the MS 7.00 driver
+ (the Genius Mouse driver supports function 0026h, but it differs
+ from the Microsoft function)
+ the TRUEDOX 4.01 driver reports its version as 6.26 through this call,
+ but as 6.24 through AX=0024h
+ There seems to be no reliable method to distinguish MS MOUSE before
+ 3.00 from mouse drivers of other vendors.
+ Some releases of the MS MOUSE 6.00 erroneously return 6.01 instead of
+ their true version number. In this case, a DI value of 01ABh can
+ be used to still detect a 6.00 driver.
+ For returned versions 6.02+, INT 33/AX=0024h should be used to retrieve
+ more accurate version data.
+ True MS MOUSE drivers can also be identified by magic numbers in
+ their copyright message, stored in the driver's segment (ES).
+ These can be found by scanning the first 2 Kb of the mouse
+ driver's segment for a string like: [new since 7.00+]
+ "** This is Copyright 1983[-19xx] Microsoft ***" with the
+ magic number stored one byte after the signature string.
+SeeAlso: AX=0024h,AX=004Dh,AX=006Ah,AX=266Ch
+
+Format of Microsoft version number:
+Offset Size Description (Table 03187)
+ 00h BYTE major version
+ 01h BYTE minor version (BCD)
+
+(Table 04087)
+Values for Microsoft MOUSE copyright string magic numbers:
+ 5564h version 3.00..6.00 (for reported versions up to 5.03, and 6.00)
+ 557Ch version 6.01Z..6.24 (for reported versions 6.01..6.24)
+ E806h version 6.25 (for reported version 6.25)
+ EB02h version 6.26..7.04 (for reported version 6.26..7.04)
+ 0800h Integrated driver 1.0+ (for reported version 9.x+)
+Note: Versions above 7.04 (except for integrated mouse drivers) have a magic
+ number representing their version number, e.g. 0507h for version 7.05
diff --git a/doc/wheelapi.txt b/doc/wheelapi.txt new file mode 100644 index 0000000..35a1aa6 --- /dev/null +++ b/doc/wheelapi.txt @@ -0,0 +1,109 @@ +
+Wheel support in DOS real-mode mouse drivers
+
+List of DOS applications which use the wheel API:
+
+Thanks to Rugxulo and others for collecting this list :-)
+
+- GVFM (graphical file manager)
+- Mpxplay (.MP3, .OGG, etc. audio player)
+- Arachne/GPL (graphical web browser / email suite)
+- Star Commander (file manager)
+- PDCurses 3.x (console library)
+- Necromancer's DOS Navigator (file manager)
+- Fred (text editor using graphics mode)
+- Blocek (graphical Unicode text editor, image viewer)
+- Hammer of Thyrion (DOS port of Hexen II game)
+- 4DOS (COMMAND.COM shell replacement)
+- Deskwork (graphical user interface, StarTrek style)
+
+---------------------------------------------------------------------
+
+API version 1.0
+
+Summary:
+
+This document describes an extension to the commonly used INT 33h Mouse
+API to add wheel (Z axis) support. This draft introduces extra functions
+and additions to the standard INT 33 API. These new and changed functions
+are mentioned in the technote.txt as the WheelAPI.
+
+---------------------------------------------------------------------
+New functions:
+
+INT 33/0011 - Check wheel support and get capabilities flags
+ AX = 0011h
+Return: AX = 574Dh ('WM' in assembly) if Wheel API is supported by driver
+ BX = Capabilities flag (all bits reserved)
+ CX = Capabilities flag
+ Bit(s) Description
+ ------ -----------
+ 0 1=Pointing device supports wheel
+ 1-15 Reserved
+Note: this function should be examined before accessing wheel features
+
+---------------------------------------------------------------------
+Changes in the original INT 33h functions:
+
+INT 33/0000 - Reset driver and read status
+Note: this call clears the wheel movement counter
+
+INT 33/0003 - Get cursor position, buttons status and wheel counter
+ AX = 0003h
+Return: BL = buttons status
+ BH = 8-bit signed counter of wheel movement since last call
+ CX = column
+ DX = row
+Notes: returned wheel counter contains all wheel movements accumulated since
+ the last call to INT 33/AX=0003h, INT 33/AX=0005h/BX=-1 or
+ INT 33/AX=0006h/BX=-1
+ positive value of wheel counter means downward wheel movement
+ this call clears the wheel movement counter
+
+INT 33/0005 - Get button press or wheel movement data
+ AX = 0005h
+ BX = button number or -1 for wheel
+Return: AL = state of buttons
+ AH = 8-bit signed counter of wheel movement
+ ---button info---
+ BX = number of times specified button has been pressed since last call
+ CX = column where specified button was last pressed
+ DX = row where specified button was last pressed
+ ---wheel info---
+ BX = 16-bit signed counter of wheel movement since last call
+ CX = column where wheel was last moved
+ DX = row where wheel was last moved
+Notes: returned wheel counters contain all wheel movements accumulated since
+ the last call to INT 33/AX=0003h, INT 33/AX=0005h/BX=-1 or
+ INT 33/AX=0006h/BX=-1
+ positive value of wheel counter means downward wheel movement
+ this call clears the wheel movement counter for BX=-1
+
+INT 33/0006 - Get button release or wheel movement data
+ AX = 0006h
+ BX = button number or -1 for wheel
+Return: AL = state of buttons
+ AH = 8-bit signed counter of wheel movement
+ ---button info---
+ BX = number of times specified button has been released since last call
+ CX = column where specified button was last released
+ DX = row where specified button was last released
+ ---wheel info---
+ BX = 16-bit signed counter of wheel movement since last call
+ CX = column where wheel was last moved
+ DX = row where wheel was last moved
+Notes: returned wheel counters contain all wheel movements accumulated since
+ the last call to INT 33/AX=0003h, INT 33/AX=0005h/BX=-1 or
+ INT 33/AX=0006h/BX=-1
+ positive value of wheel counter means downward wheel movement
+ this call clears the wheel movement counter for BX=-1
+
+INT 33/000C - Define User Interrupt Routine
+INT 33/0014 - Exchange User Interrupt Routines
+Notes: on entry, bit 7 of CX (call mask) indicates that the user routine
+ will be called on a wheel movement
+ the user routine will be called with BH holding the 8-bit signed
+ counter of wheel movement since the last call to the routine
+
+INT 33/0021 - Software reset
+Note: this call clears the wheel movement counter
diff --git a/doc/whlapi2.txt b/doc/whlapi2.txt new file mode 100644 index 0000000..c2b087f --- /dev/null +++ b/doc/whlapi2.txt @@ -0,0 +1,69 @@ +This document describes an extension to the CuteMouse wheelapi.txt +to support two wheels (vertical and horizontal). + +NOTE: Draft, subject to change. + +--------------------------------------------------------------------- +Changes to the wheelapi v1 functions: + +INT 33/0011 - Check wheel support and get capabilities flags + AX = 0011h +Return: AX = 574Dh ('WM' in assembly) if Wheel API is supported by driver + CX = Capabilities flag + Bit(s) Description + ------ ----------- + 0 1=Pointing device supports wheel + 1 1=Pointing device supports 2nd wheel + 2-15 Reserved +Notes: this function should be examined before accessing wheel features. + vbmouse currently assumes this is called after each int33/0, otherwise + wheel events may be sent as keystrokes instead of wheelapi. + +INT 33/0003 - Get cursor position, buttons status and wheel counter + AX = 0003h +Return: BL = buttons status (bits 0-4) + CX = column + DX = row + BH = 8-bit signed counter of wheel movement since last call. + positive value means downward wheel movement. + AH = 8-bit signed counter of 2nd wheel movement since last call. + positive value means rightward wheel movement. +Note: calling this clears the wheel counter for ALL wheels. + +INT 33/0005 - Get button press or wheel movement data + AX = 0005h + BX = button number, -1 for vertical wheel, -2 for 2nd/horizontal wheel + (return values remain the same) +Note: as in wheelapi v1, AH always contains (1st) wheel movement on return + independently of button number requested in BX. + +INT 33/0006 - Get button release or wheel movement data + AX = 0006h + BX = button number, -1 for vertical wheel, -2 for 2nd/horizontal wheel + (return values remain the same) + +INT 33/000C - Define User Interrupt Routine +INT 33/0014 - Exchange User Interrupt Routines +Bitfields for mouse call mask: +Bit(s) Description + 0-6 same as Table 03171 (as in int33.lst) + 7 vertical wheel movement (as in wheelapi.txt) + 8 (absolute mouse event bit) + 9 horizontal wheel movement + 10 4th button pressed + 11 4th button released + 12 5th button pressed + 13 5th button released + 14-15 unused +Notes: on entry, bit 9 of CX (call mask) indicates that the user routine + will be called on horizontal wheel movement + if the user routine is called with bit 9 of AX (condition mask) set, + then BH will hold the 8-bit signed counter of HORIZONTAL wheel + movement since the last call to the routine. if bit 7 is set, then + BH holds VERTICAL wheel movement. + it is impossible for the user routine to be called with both + vertical (bit 7) and horizontal (bit 9) movement. + A program that just sets 0xFFFF event mask and expects to find vertical + wheel movement info in BH (wheelapi v1 style) will be confused, as + here when bit 9 is set BH will contain horizontal wheel movement + instead. DN/2 does this, so horizontal scrolling acts like vertical. @@ -23,34 +23,7 @@ #include <stdint.h> #include <conio.h> -#define BIOS_DATA_AREA_SEGMENT 0x40 - -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; -} - -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_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_cur_video_page() bda_get_word(0x62) -#define bda_get_last_row() bda_get_byte(0x84) -#define bda_get_char_height() bda_get_word(0x85) - -#define bda_get_tick_count() bda_get_dword(0x6c) -#define bda_get_tick_count_lo() bda_get_word(0x6c) +#include "bda.h" enum videotype { VIDEO_UNKNOWN, @@ -22,6 +22,8 @@ #include <stdbool.h> #include <stdint.h> +#include "utils.h" +#include "bda.h" /** Standard PS/2 mouse IRQ. At least on VirtualBox. */ #define PS2_MOUSE_IRQ 12 @@ -49,19 +51,27 @@ enum ps2m_status { enum ps2m_packet_size { PS2M_PACKET_SIZE_STREAMING = 1, - PS2M_PACKET_SIZE_PLAIN = 3, + PS2M_PACKET_SIZE_STD = 3, PS2M_PACKET_SIZE_EXT = 4, }; enum ps2m_device_ids { /** Standard PS/2 mouse, 2 buttons. */ - PS2M_DEVICE_ID_PLAIN = 0, + PS2M_DEVICE_ID_STD = 0, /** IntelliMouse PS/2, with wheel. */ - PS2M_DEVICE_ID_IMPS2 = 3, + PS2M_DEVICE_ID_IMPS2 = 3, /** IntelliMouse Explorer, wheel and 5 buttons. */ - PS2M_DEVICE_ID_IMEX = 4, + PS2M_DEVICE_ID_IMEX = 4, /** IntelliMouse Explorer, wheel, 5 buttons, and horizontal scrolling. */ - PS2M_DEVICE_ID_IMEX_HORZ = 5 + PS2M_DEVICE_ID_IMEX_HORZ = 5 +}; + +/** Additional bits set in 4th byte of IMEX protocol. */ +enum ps2m_imex_ext_byte_bits { + PS2M_IMEX_BUTTON_4 = 1 << 4, + PS2M_IMEX_BUTTON_5 = 1 << 5, + PS2M_IMEX_HORIZONTAL_SCROLL = 1 << 6, + PS2M_IMEX_VERTICAL_SCROLL = 1 << 7 }; /** Valid PS/2 mouse resolutions in DPI. */ @@ -86,6 +96,12 @@ enum ps2m_sample_rate { /** Invoked by the BIOS when there is a mouse event. */ typedef void (__far * LPFN_PS2CALLBACK)(); +static bool ps2m_installed() +{ + uint16_t equipment = bda_get_equipment(); + return equipment & (1 << 2); +} + static ps2m_err ps2m_init(uint8_t packet_size); #pragma aux ps2m_init = \ "stc" /* If nothing happens, assume failure */ \ @@ -200,7 +216,7 @@ static ps2m_err ps2m_enable(bool enable); __value [ah] \ __modify [ax] -/** Sends the magic sequence to switch the mouse to the IMPS2 protocol. */ +/** Sends the magic sequence to switch the mouse to the IntelliMouse protocol. */ static void ps2m_send_imps2_sequence(void) { ps2m_set_sample_rate(PS2M_SAMPLE_RATE_200); @@ -208,8 +224,28 @@ static void ps2m_send_imps2_sequence(void) ps2m_set_sample_rate(PS2M_SAMPLE_RATE_80); } -/** Detects whether we have a IMPS2 mouse with wheel support. */ -static bool ps2m_detect_wheel(void) +/** Sends the magic sequence to switch the mouse to the IntelliMouse Explorer protocol. */ +static void ps2m_send_imex_sequence(void) +{ + ps2m_set_sample_rate(PS2M_SAMPLE_RATE_200); + ps2m_set_sample_rate(PS2M_SAMPLE_RATE_200); + ps2m_set_sample_rate(PS2M_SAMPLE_RATE_80); +} + +/** Sends the magic sequence to switch the mouse to the IntelliMouse Explorer + * with horz. wheel protocol. Note: unclear if mouse changes device_id after this. */ +static void ps2m_send_imex_horz_sequence(void) +{ + ps2m_set_sample_rate(PS2M_SAMPLE_RATE_200); + ps2m_set_sample_rate(PS2M_SAMPLE_RATE_80); + ps2m_set_sample_rate(PS2M_SAMPLE_RATE_40); +} + +/** Detects whether we have a IMPS2 mouse with wheel support. + * + * @return true if we have at least imps/2 with 1 wheel support. + */ +static bool ps2m_detect_imps2(void) { int err; uint8_t device_id; @@ -220,14 +256,14 @@ static bool ps2m_detect_wheel(void) return false; } - if (device_id == PS2M_DEVICE_ID_IMPS2) { + if (device_id == PS2M_DEVICE_ID_IMPS2 + || device_id == PS2M_DEVICE_ID_IMEX + || device_id == PS2M_DEVICE_ID_IMEX_HORZ) { // Already wheel return true; } - if (device_id != PS2M_DEVICE_ID_PLAIN) { - // TODO: Likely we have to accept more device IDs here - dprintf("Unknown initial mouse device_id=0x%x\n", device_id); + if (device_id != PS2M_DEVICE_ID_STD) { return false; } @@ -236,8 +272,36 @@ static bool ps2m_detect_wheel(void) // Now check if the device id has changed err = ps2m_get_device_id(&device_id); - return err == 0 && device_id == PS2M_DEVICE_ID_IMPS2; } +static bool ps2m_detect_imex(void) +{ + int err; + uint8_t device_id; + + // Get the initial mouse device id + err = ps2m_get_device_id(&device_id); + if (err) { + return false; + } + + if (device_id == PS2M_DEVICE_ID_IMEX + || device_id == PS2M_DEVICE_ID_IMEX_HORZ) { + // Already ImEx + return true; + } + + if (device_id != PS2M_DEVICE_ID_IMPS2) { + return false; + } + + // Send the knock sequence to activate the extended packet + ps2m_send_imex_sequence(); + + // Now check if the device id has changed + err = ps2m_get_device_id(&device_id); + return err == 0 && device_id == PS2M_DEVICE_ID_IMEX; +} + #endif /* INT15PS2_H */ @@ -1,11 +1,11 @@ #ifndef INT16KBD_H #define INT16KBD_H -static bool int16_store_keystroke(uint16_t scancode); +static bool int16_store_keystroke(uint8_t scancode, uint8_t character); #pragma aux int16_store_keystroke = \ "mov ah, 0x05" \ "int 0x16" \ - __parm [cx] \ + __parm [ch] [cl] \ __value [al] \ __modify [ax] @@ -87,6 +87,14 @@ enum INT33_API { * @param cx horizontal speed, dx vertical speed */ INT33_SET_MOUSE_SPEED = 0xF, + /** Configures graphicsmode mouse cursor, but allows custom/larger sizes. + * @param bh cursor width (in words) + * @param ch cursor height (rows) + * @param bx horizontal hotspot , cx vertical hotspot + * @param es:dx address of cursor shape bitmap + * @see INT33_SET_GRAPHICS_CURSOR.*/ + INT33_SET_LARGE_GRAPHICS_CURSOR = 0x12, + /** If the mouse is moved more than this mickeys in one second, * the mouse motion is doubled. * @param cx doubling threshold (mickeys per second) */ @@ -150,7 +158,8 @@ enum INT33_API { }; enum INT33_CAPABILITY_BITS { - INT33_CAPABILITY_MOUSE_API = 1 << 0 + INT33_CAPABILITY_WHEEL_API = 1 << 0, + INT33_CAPABILITY_WHEEL2_API = 1 << 1 }; #define INT33_WHEEL_API_MAGIC 'WM' @@ -167,11 +176,14 @@ enum INT33_MOUSE_TYPE { enum INT33_BUTTON_MASK { INT33_BUTTON_MASK_LEFT = 1 << 0, INT33_BUTTON_MASK_RIGHT = 1 << 1, - INT33_BUTTON_MASK_CENTER = 1 << 2 + INT33_BUTTON_MASK_CENTER = 1 << 2, + INT33_BUTTON_MASK_4TH = 1 << 3, + INT33_BUTTON_MASK_5TH = 1 << 4, }; enum INT33_EVENT_MASK { INT33_EVENT_MASK_MOVEMENT = 1 << 0, + INT33_EVENT_MASK_LEFT_BUTTON_PRESSED_INDEX = 1, INT33_EVENT_MASK_LEFT_BUTTON_PRESSED = 1 << 1, INT33_EVENT_MASK_LEFT_BUTTON_RELEASED = 1 << 2, INT33_EVENT_MASK_RIGHT_BUTTON_PRESSED = 1 << 3, @@ -182,6 +194,17 @@ enum INT33_EVENT_MASK { // Wheel API Extensions: /** Wheel mouse movement. */ INT33_EVENT_MASK_WHEEL_MOVEMENT = 1 << 7, + /** 2nd/horizontal wheel mouse movement. */ + INT33_EVENT_MASK_HORIZ_WHEEL_MOVEMENT = 1 << 9, + + INT33_EVENT_MASK_ANY_WHEEL_MOVEMENT + = INT33_EVENT_MASK_WHEEL_MOVEMENT | INT33_EVENT_MASK_HORIZ_WHEEL_MOVEMENT, + + INT33_EVENT_MASK_4TH_BUTTON_PRESSED_INDEX = 10, + INT33_EVENT_MASK_4TH_BUTTON_PRESSED = 1 << 10, + INT33_EVENT_MASK_4TH_BUTTON_RELEASED = 1 << 11, + INT33_EVENT_MASK_5TH_BUTTON_PRESSED = 1 << 12, + INT33_EVENT_MASK_5TH_BUTTON_RELEASED = 1 << 13, // Absolute API extensions: /** The source of the event is an absolute pointing device. */ @@ -199,6 +222,29 @@ static uint16_t int33_reset(void); __value [ax] \ __modify [ax bx] +static bool int33_reset_get_buttons(uint16_t *num_buttons); +#pragma aux int33_reset_get_buttons = \ + "mov ax, 0x0" \ + "int 0x33" \ + "mov [di], bx" \ + "cmp ax, 0xFFFF" \ + "sete ah" \ + __parm [di] \ + __value [ah] \ + __modify [ax bx] + +static void int33_show_cursor(void); +#pragma aux int33_show_cursor = \ + "mov ax, 0x1" \ + "int 0x33" \ + __modify [ax] + +static void int33_hide_cursor(void); +#pragma aux int33_hide_cursor = \ + "mov ax, 0x2" \ + "int 0x33" \ + __modify [ax] + static void int33_set_horizontal_window(int16_t min, int16_t max); #pragma aux int33_set_horizontal_window = \ "mov ax, 0x7" \ @@ -227,6 +273,13 @@ static void int33_set_mouse_speed(int16_t x, int16_t y); __parm [cx] [dx] \ __modify [ax] +static void int33_set_speed_double_threshold(uint16_t th); +#pragma aux int33_set_speed_double_threshold = \ + "mov ax, 0x13" \ + "int 0x33" \ + __parm [dx] \ + __modify [ax] + static uint16_t int33_get_mouse_status_size(void); #pragma aux int33_get_mouse_status_size = \ "mov bx, 0" \ @@ -256,6 +309,18 @@ static void int33_set_sensitivity(uint16_t sens_x, uint16_t sens_y, uint16_t dou __parm [bx] [cx] [dx] \ __modify [ax] +static void int33_get_sensitivity(uint16_t *sens_x, uint16_t *sens_y, uint16_t *double_speed_threshold); +#pragma aux int33_get_sensitivity = \ + "push dx" \ + "mov ax, 0x1B" \ + "int 0x33" \ + "mov [si], bx" \ + "mov [di], cx" \ + "pop bx" \ + "mov [bx], dx" \ + __parm [si] [di] [dx] \ + __modify [ax bx cx dx] + static uint16_t int33_get_driver_version(void); #pragma aux int33_get_driver_version = \ "mov bx, 0" \ @@ -294,4 +359,14 @@ static uint16_t int33_get_capabilities(void); __value [cx] \ __modify [ax bx cx] +static const char __far * int33_get_version_string(void); +#pragma aux int33_get_version_string = \ + "xor ax, ax" \ + "mov di, ax" \ + "mov es, ax" \ + "mov ax, 0x6d" \ + "int 0x33" \ + __value [es di] \ + __modify [ax] + #endif /* INT33_H */ @@ -1,722 +1,190 @@ /* Functions that emulate UNIX catgets */ -/* Copyright (C) 1999,2000,2001 Jim Hall <jhall@freedos.org> */ -/* Kitten version 2003 by Tom Ehlert, heavily modified by Eric Auer 2003 */ +/* Kitten version 2021 by Tom Ehlert, */ /* - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + This software is free software; free to use, + modify, pass to others, whatever + + use it at your own risk */ +/* Minor modifications to use internal static buffer to keep API compatible + * with regular kitten. */ + #ifndef NO_KITTEN #include <stdio.h> /* sprintf */ -#ifndef _MICROC_ -#include <stdlib.h> /* getenv */ -#include <string.h> /* strchr */ -#include <dos.h> -#ifndef __PACIFIC__ -#include <fcntl.h> -#else -#define O_RDONLY 0 -#define O_TEXT 0 -#endif -#else -#include <intr.h> -#include <file.h> -#define O_RDONLY READONLY -#define O_TEXT 0 -#endif -/* assert we are running in small model */ -/* else pointer below has to be done correctly */ -/* char verify_small_pointers[sizeof(void*) == 2 ? 1 : -1]; */ +#include <stdlib.h> /* getenv */ +#include <io.h> /* sprintf */ +#include <fcntl.h> /* sprintf */ +#include <string.h> /* sprintf */ #include "kitten.h" - -char catcontents[8192]; - -struct catstring -{ - char key1; - char key2; - char *text; -}; - -/* Micro-C does not support typedef */ -#define catstring_t struct catstring - -catstring_t catpoints[128]; - - -/* Local prototypes */ - -int catread (char *catfile); /* Reads a catfile into the hash */ -char *processEscChars (char *line); /* Converts c escape sequences to chars */ - -int get_char (int file); /* not meant for external use */ -/* external use would cause consistency problems if */ -/* value or related file of the file handle changes */ - -int mystrtoul (char *src, int base, int size); - +#include "kittenc.h" /* Globals */ -nl_catd _kitten_catalog = 0; /* _kitten_catalog descriptor or 0 */ -char catfile[_MAX_PATH]; /* full path to _kitten_catalog */ - -char getlbuf[8192]; /* read buffer for better speed */ -char *getlp; /* current point in buffer */ -int getlrem = -1; /* remaining bytes in buffer */ -char lastcr = 0; /* for 2byte CR LF sequences */ - - -#ifndef _MICROC_ -#ifndef __DJGPP__ +static char global_nls_buffer[4096] = {0}; -/* DOS handle based file usage */ -int -dos_open (char *filename) -{ - union REGS r; - struct SREGS s; - - r.h.ah = 0x3d; - r.h.al = 0; /* read mode only supoported now !! */ - r.x.dx = FP_OFF (filename); - s.ds = FP_SEG (filename); - intdosx (&r, &r, &s); - return ((r.x.cflag) ? -1 : (int) r.x.ax); -} - - -int -dos_read (int file, void *ptr, unsigned count) -{ - union REGS r; - struct SREGS s; - r.h.ah = 0x3f; - r.x.bx = file; - r.x.cx = count; - r.x.dx = FP_OFF (ptr); - s.ds = FP_SEG (ptr); - intdosx (&r, &r, &s); - return ((r.x.cflag) ? 0 : r.x.ax); -} - - -int -dos_write (int file, void *ptr, unsigned count) -{ - union REGS r; - struct SREGS s; - r.h.ah = 0x40; - r.x.bx = file; - r.x.cx = count; - r.x.dx = FP_OFF (ptr); - s.ds = FP_SEG (ptr); - intdosx (&r, &r, &s); - return ((r.x.cflag) ? 0 : r.x.ax); -} - - -void -dos_close (int file) -{ - union REGS r; - r.h.ah = 0x3e; - r.x.bx = file; - intdos (&r, &r); -} - -#endif /*DJGPP*/ -#endif /*Micro-C */ /* Functions */ /** * On success, catgets() returns a pointer to an internal * buffer area containing the null-terminated message string. * On failure, catgets() returns the value 'message'. */ -char * -kittengets (int setnum, int msgnum, char *message) -{ -/* In Micro-C, variables must be defined at the start of the - * function and may not be immediately assigned a value - */ -#ifdef _MICROC_ - int i; - i = 0; -#else - int i = 0; -#endif - - while ((catpoints[i].key1 != setnum) || (catpoints[i].key2 != msgnum)) - { - if ((catpoints[i].text == NULL) || (i > 127)) /* at EOF */ - return message; - i++; - } - - if (catpoints[i].text == NULL) - return message; - else - return (catpoints[i].text); -} -/** - * Obtains path to current exec. - */ -static char * get_cmd_path(char *buf) +const char * +_kittengets (int msgID, const char *message) { - char cmd_drive[_MAX_DRIVE]; - char cmd_dir[_MAX_DIR]; + struct message_header *message_header; // if this is != 0 then init was done - _splitpath(__argv[0], cmd_drive, cmd_dir, NULL, NULL); - _makepath(buf, cmd_drive, cmd_dir, "", NULL); - return buf; -} + if (global_nls_buffer[0] == 0) + return message; + message_header = (struct message_header *)global_nls_buffer; + + for ( ; message_header->len != 0; message_header = (struct message_header *)((char*)message_header + message_header->len)) + { + if (message_header->id== msgID) + { + return (const char*)message_header + sizeof(struct message_header); + } -/** - * Initialize kitten for program (name). - */ - -nl_catd -kittenopen (char *name) -{ - /* catopen() returns a message _kitten_catalog descriptor * - * of type nl_catd on success. On failure, it returns -1. */ - - char catlang[3]; /* from LANG environment var. */ - char *nlsptr; /* ptr to NLSPATH */ - char *lang; /* ptr to LANG */ - int i; -#ifdef _MICROC_ - char *tok; - int toklen; -#endif - - /* Open the _kitten_catalog file */ - /* The value of `_kitten_catalog' will be set based on catread */ - - if (_kitten_catalog) - { /* Already one open */ - write (1, "cat already open\r\n", strlen ("cat already open\r\n")); - return (-1); - } - - for (i = 0; i < 128; i++) - catpoints[i].text = NULL; - - if (strchr (name, '\\')) - { - /* unusual case: 'name' is a filename */ - write (1, "found \\\r\n", 9); - _kitten_catalog = catread (name); - if (_kitten_catalog) - return (_kitten_catalog); - } - - /* If the message _kitten_catalog file name does not contain a directory * - * separator, then we need to try to locate the message _kitten_catalog. */ - - /* We will need the value of LANG, and may need a 2-letter abbrev of - LANG later on, so get it now. */ - - lang = getenv ("LANG"); - - if (lang == NULL) - { - /* printf("no lang= found\n"); *//* not fatal, though */ - /* Return failure - we won't be able to locate the cat file */ - return (-1); - } - - if ((strlen (lang) < 2) || ((strlen (lang) > 2) && (lang[2] != '-'))) - { - /* Return failure - we won't be able to locate the cat file */ - return (-1); - } - - memcpy (catlang, lang, 2); - /* we copy the full LANG value or the part before "-" if "-" found */ - catlang[2] = '\0'; - - - /* first try to find catalog file in the same path as exe */ - get_cmd_path(catfile); - - strcat (catfile, name); - strcat (catfile, "."); - strcat (catfile, catlang); - _kitten_catalog = catread (catfile); - if (_kitten_catalog) - return (_kitten_catalog); - - - /* otherwise step through NLSPATH */ - - nlsptr = getenv ("NLSPATH"); - - if (nlsptr == NULL) - { - /* printf("no NLSPATH= found\n"); *//* not fatal either */ - /* Return failure - we won't be able to locate the cat file */ - return (-1); - } - - catfile[0] = '\0'; - - while (nlsptr && nlsptr[0]) - { -#ifdef _MICROC_ - tok = strchr (nlsptr, ';'); -#else - char *tok = strchr (nlsptr, ';'); - int toklen; -#endif - - if (tok == NULL) - toklen = strlen (nlsptr); /* last segment */ - else - toklen = tok - nlsptr; /* segment terminated by ';' */ - - /* catfile = malloc(toklen+1+strlen(name)+1+strlen(lang)+1); */ - /* Try to find the _kitten_catalog file in each path from NLSPATH */ - - if ((toklen + 6 + strlen (name)) > sizeof (catfile)) - { - write (1, "NLSPATH overflow\r\n", strlen ("NLSPATH overflow\r\n")); - return 0; /* overflow in NLSPATH, should never happen */ - } - - /* Rule #1: %NLSPATH%\%LANG%\cat */ - - memcpy (catfile, nlsptr, toklen); - strcpy (catfile + toklen, "\\"); - strcat (catfile, catlang); - strcat (catfile, "\\"); - strcat (catfile, name); - _kitten_catalog = catread (catfile); - if (_kitten_catalog) - return (_kitten_catalog); - - /* Rule #2: %NLSPATH%\cat.%LANG% */ - - /* memcpy(catfile, nlsptr, toklen); */ - strcpy (catfile + toklen, "\\"); - strcat (catfile, name); - strcat (catfile, "."); - strcat (catfile, catlang); - _kitten_catalog = catread (catfile); - if (_kitten_catalog) - return (_kitten_catalog); - - /* Grab next tok for the next while iteration */ - - nlsptr = tok; - if (nlsptr) - nlsptr++; - - } /* while tok */ - - /* We could not find it. Return failure. */ + } - return (-1); + return message; } /** - * Load a message catalog into memory. + * Initialize kitten for program (name). */ -int -catread (char *catfile) +#define _toupper(c) (c & ~0x20) + +nl_catd +kittenopen (const char *name) { - int file; /* pointer to the catfile */ - int i; - char *where; - char *tok; -#ifdef _MICROC_ - char *msg; - char *key; - int key1; - int key2; -#endif - - /* Get the whole catfile into a buffer and parse it */ - - file = open (catfile, O_RDONLY | O_TEXT); - if (file < 0) - /* Cannot open the file. Return failure */ - return 0; - - for (i = 0; i < 128; i++) - catpoints[i].text = NULL; - - for (i = 0; (unsigned int) i < sizeof (catcontents); i++) - catcontents[i] = '\0'; - - /* Read the file into memory */ - i = read (file, catcontents, sizeof (catcontents) - 1); - - if ((i == sizeof (catcontents) - 1) || (i < 1)) - return 0; /* file was too big or too small */ - - where = catcontents; - i = 0; /* catpoints entry */ - - do - { -#ifndef _MICROC_ - char *msg; - char *key; - int key1 = 0; - int key2 = 0; -#else - key1 = 0; - key2 = 0; -#endif - - tok = strchr (where, '\n'); - - if (tok == NULL) - { /* done? */ - close (file); - return 1; /* success */ - } - - tok[0] = '\0'; /* terminate here */ - tok--; /* guess: \r before \n */ - if (tok[0] != '\r') - tok++; /* if not, go back */ - else - { - tok[0] = '\0'; /* terminate here already */ - tok++; - } - tok++; /* this is where the next line starts */ - - if ((where[0] >= '0') && (where[0] <= '9') && - ((msg = strchr (where, ':')) != NULL)) - { - /* Skip everything which starts with # or with no digit */ - /* Entries look like "1.2:This is a message" */ - - msg[0] = '\0'; /* remove : */ - msg++; /* go past the : */ - - if ((key = strchr (where, '.')) != NULL) - { - key[0] = '\0'; /* turn . into terminator */ - key++; /* go past the . */ - key1 = mystrtoul (where, 10, strlen (where)); - key2 = mystrtoul (key, 10, strlen (key)); - - if ((key1 >= 0) && (key2 >= 0)) + int fd; + struct message_end message_end; + struct content content[KITTEN_MAX_RESOURCES]; + char *language; + int i; + long len; + char exename[_MAX_PATH]; + long fileendnow, seekoffset; + + //printf("kittenopen %s\n", name); + + if ((fd = open(name, _O_RDONLY | _O_BINARY)) < 0) { - catpoints[i].key1 = key1; - catpoints[i].key2 = key2; - catpoints[i].text = processEscChars (msg); - if (catpoints[i].text == NULL) /* ESC parse error */ - catpoints[i].text = msg; - i++; /* next entry! */ - } /* valid keys */ - - } /* . found */ - - } /* : and digit found */ - - where = tok; /* go to next line */ + sprintf(exename, "%s.exe", name); + if ((fd = open(exename, _O_RDONLY | _O_BINARY)) < 0) + { + printf("can't open %s\n", name); // should never happen + return 0; + } + } - } - while (1); -#ifdef __PACIFIC__ - return 0; -#endif -} + fileendnow = lseek(fd, 0, SEEK_END); + lseek(fd, - (int)sizeof(struct message_end), SEEK_END); -void -kittenclose (void) -{ - /* close a message _kitten_catalog */ - _kitten_catalog = 0; -} + read(fd, &message_end, sizeof(struct message_end)); + if (memcmp(message_end.ID, "KITTENC", 8) != 0) + { + //printf("no KITTENC record found\n"); + return 0; + } -/** - * Parse a string that represents an unsigned integer. - * Returns -1 if an error is found. The first size - * chars of the string are parsed. - */ - -int -mystrtoul (char *src, int base, int size) -{ -#ifdef _MICROC_ - int ret; - int digit; - int ch; - ret = 0; -#else - int ret = 0; -#endif - - for (; size > 0; size--) - { -#ifdef _MICROC_ - ch = *src; -#else - int digit; - int ch = *src; -#endif - src++; - - if (ch >= '0' && ch <= '9') - digit = ch - '0'; - else if (ch >= 'A' && ch <= 'Z') - digit = ch - 'A' + 10; - else if (ch >= 'a' && ch <= 'z') - digit = ch - 'a' + 10; - else - return -1; - - if (digit >= base) - return -1; - - ret = ret * base + digit; - } /* for */ - - return ret; -} + if (message_end.resource_count > KITTEN_MAX_RESOURCES) + { + printf("resource > %d\n", KITTEN_MAX_RESOURCES); + return 0; + } + seekoffset = fileendnow - message_end.fileend_orig; -/** - * Process strings, converting \n, \t, \v, \b, \r, \f, \\, - * \ddd, \xdd and \x0dd to actual chars. - * (Note: \x is an extension to support hexadecimal) - * This is used to allow the messages to use c escape sequences. - * Modifies the line in-place (always same size or shorter). - * Returns a pointer to input string. - */ + if (lseek(fd, message_end.filepos+seekoffset, SEEK_SET) != message_end.filepos+seekoffset) + { + printf("can't seek 1\n"); + return 0; + } -char * -processEscChars (char *line) -{ - /* used when converting \xdd and \ddd (hex or octal) characters */ - char ch; -#ifdef _MICROC_ - char *src; - char *dst; - int chx; - src = line; - dst = line; -#else - char *src = line; - char *dst = line; /* possible as dst is shorter than src */ -#endif - - if (line == NULL) - return line; - - /* cycle through copying characters, except when a \ is encountered. */ - while (*src != '\0') - { - ch = *src; - src++; - - if (ch == '\\') - { - ch = *src; /* what follows slash? */ - src++; - - switch (ch) - { - case '\\': /* a single slash */ - *dst = '\\'; - dst++; - break; - case 'n': /* a newline (linefeed) */ - *dst = '\n'; - dst++; - break; - case 'r': /* a carriage return */ - *dst = '\r'; - dst++; - break; - case 't': /* a horizontal tab */ - *dst = '\t'; - dst++; - break; - case 'v': /* a vertical tab */ - *dst = '\v'; - dst++; - break; - case 'b': /* a backspace */ - *dst = '\b'; - dst++; - break; - case 'a': /* alert */ - *dst = '\a'; - dst++; - break; - case 'f': /* formfeed */ - *dst = '\f'; - dst++; - break; - case 'x': /* extension supporting hex numbers \xdd or \x0dd */ - { -#ifdef _MICROC_ - chx = mystrtoul (src, 16, 2); /* get value */ -#else - int chx = mystrtoul (src, 16, 2); /* get value */ -#endif - if (chx >= 0) - { /* store character */ - *dst = chx; - dst++; - src += 2; - } - else /* error so just store x (loose slash) */ - { - *dst = *src; - dst++; - } - } - break; - default: /* just store letter (loose slash) or handle octal */ - { -#ifdef _MICROC_ - chx = mystrtoul (src, 8, 3); /* get value */ -#else - int chx = mystrtoul (src, 8, 3); /* get value */ -#endif - if (chx >= 0) - { /* store character */ - *dst = chx; - dst++; - src += 3; - } - else - { - *dst = *src; - dst++; - } - } - break; - } /* switch */ - } /* if backslash */ - else - { - *dst = ch; - dst++; - } - } /* while */ - - /* ensure '\0' terminated */ - *dst = '\0'; - - return line; -} + read(fd, &content, sizeof(content[0]) * message_end.resource_count); -int -get_char (int file) -{ -#ifdef _MICROC_ - int rval; - rval = -1; -#else - int rval = -1; -#endif - - if (getlrem <= 0) - { /* (re)init buffer */ - getlrem = read (file, getlbuf, sizeof (getlbuf)); - if (getlrem <= 0) - return -1; /* fail: read error / EOF */ - getlp = getlbuf; /* init pointer */ - } - - if (getlrem > 0) - { /* consume byte from buffer */ - rval = getlp[0]; - getlp++; - getlrem--; - } - - return rval; -} + language = getenv("LANG"); + // printf("using language %s\n", language); -/** - * Read a line of text from file. You must call this with - * a null buffer or null size to flush buffers when you are - * done with a file before using it on the next file. Cannot - * be used for 2 files at the same time. - */ + if (message_end.resource_count == 1) + { + if (language && + _toupper(language[0]) == 'E' && + _toupper(language[0]) == 'N') + { + // set LANG=EN switches to internal strings + close(fd); + return 0; + } + // otherwise use the only compiled language + i = 0; + + goto found_my_language; + } + + if (language == NULL) // language not found + { // use internal strings + close(fd); + return 0; + } + + // see if we find our language in the existing resources + for (i = 0; i < message_end.resource_count; i++) + { + if(_toupper(content[i].language[0]) == _toupper(language[0]) && + _toupper(content[i].language[1]) == _toupper(language[1])) + { + goto found_my_language; + } + } -int -get_line (int file, char *str, int size) -{ - int ch; -#ifdef _MICROC_ - int success; - success = 0; -#else - int success = 0; -#endif + // language not found + close(fd); + return 0; - if ((size == 0) || (str == NULL)) - { /* re-init get_line buffers */ - getlp = getlbuf; - getlrem = -1; - lastcr = 0; - return 0; - } + - str[0] = '\0'; - while ((size > 0) && (success == 0)) - { - ch = get_char (file); - if (ch < 0) - break; /* (can cause fail if no \n found yet) */ +found_my_language: + // found our language + // printf(" language %s found between %lx and %lx\n", language, content[i].filepos_start, content[i].filepos_end); - if (ch == '\r') - ch = get_char (file); /* ignore \r */ + // printf("seek %lu --> %lu\n", content[i].filepos_start, content[i].filepos_start+seekoffset); - str[0] = ch; + lseek(fd, content[i].filepos_start+seekoffset, SEEK_SET); + len = content[i].filepos_end - content[i].filepos_start; - if ((ch == '\n') || (ch == '\r')) - { /* done? */ - str[0] = '\0'; - return 1; /* success */ - } + if (len > sizeof(global_nls_buffer)) + { + printf("nls_buffer too small(%d). we need at least %l\n", + sizeof(global_nls_buffer), len); + close(fd); + return 1; + } - str++; - size--; - } /* while */ + read(fd, global_nls_buffer, (int)len); - str[0] = '\0'; /* terminate buffer */ + close(fd); + return 0; +} - return success; -} #endif /*NO_KITTEN */ @@ -20,14 +20,16 @@ */ -#ifndef _CATGETS_H -#define _CATGETS_H +#ifndef KITTEN_H +#define KITTEN_H #ifdef __cplusplus extern "C" { #endif +#define _(catalog,messageid,message) kittengets(catalog,messageid,message) + #ifdef NO_KITTEN #define kittengets(x,y,z) (z) @@ -42,39 +44,19 @@ extern "C" /* Functions */ -#define catgets(catalog, set,message_number,message) kittengets(set,message_number,message) -#define catopen(name,flag) kittenopen(name) -#define catclose(catalog) kittenclose() - -#define _(set,message_number,message) kittengets(set,message_number,message) - - - char *kittengets (int set_number, int message_number, char *message); - nl_catd kittenopen (char *name); - void kittenclose (void); - - int get_line (int file, char *buffer, int size); - -#ifndef _MICROC_ -#ifndef __DJGPP__ - - int dos_open (char *filename); -#define open(filename,mode) dos_open(filename) - - int dos_read (int file, void *ptr, unsigned count); -#define read(file, ptr, count) dos_read(file,ptr,count) +#define catgets(catalog, set,message_number,message) _kittengets((catalog << 8) + set,default_message) +#define catopen(name,flag) kittenopen(argv[0]) +#define catclose(catalog) - int dos_write (int file, void *ptr, unsigned count); -#define write(file, ptr, count) dos_write(file,ptr,count) +#define kittengets(catalog, messageid,message) _kittengets((catalog << 8) + messageid,message) +#define kittenclose(catalog) - void dos_close (int file); -#define close(file) dos_close(file) + const char *_kittengets (int messageid, const char *message); + nl_catd kittenopen (const char *exename); -#endif /*DJGPP*/ -#endif /*Micro-C */ #endif /*NO_KITTEN */ #ifdef __cplusplus } #endif -#endif /* _CATGETS_H */ +#endif /* KITTEN_H */ diff --git a/kittenc.c b/kittenc.c new file mode 100644 index 0000000..79011ea --- /dev/null +++ b/kittenc.c @@ -0,0 +1,548 @@ +/* + KITTENC - compile kitten files and attach this to + executables in a way that the kitten library retrieves + the language resources, in the same way that + catgets/kittengets ever did + +*/ +/* + This software is free software; free to use, + modify, pass to others, whatever + + use it at your own risk +*/ +/* Minor modifications to compile on UNIX host */ + + +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <string.h> +#include <io.h> + +#include "kitten.h" +#include "kittenc.h" +#include "kitten.c" // Small hack to keep everything in one file + +void usage() + { + printf(kittengets(1,0,"KITTENC - KITTEN compiler\n")); + printf(kittengets(1,1,"usage\n")); + printf(kittengets(1,2,"KITTENC program.exe ATTACH NLS\\program.??\n")); + printf(kittengets(1,3,"KITTENC program.exe ATTACH NLS\\program.DE\n")); + printf(kittengets(1,4,"KITTENC program.exe ATTACH NLS\\program.DE NLS\\program.fr\n")); + printf(kittengets(1,5,"KITTENC program.exe INFO : show info about language resources\n")); + printf(kittengets(1,6,"KITTENC program.exe DUMP : recreate language resources\n")); + printf(kittengets(1,7,"KITTENC program.exe TRUNCATE : delete attached resources\n")); + } + + + +/** + * Parse a string that represents an unsigned integer. + * Returns -1 if an error is found. The first size + * chars of the string are parsed. + */ + +int +mystrtoul (char *src, int base, int size) +{ + int ret = 0; + + for (; size > 0; size--) + { + int digit; + int ch = *src; + + src++; + + if (ch >= '0' && ch <= '9') + digit = ch - '0'; + else if (ch >= 'A' && ch <= 'Z') + digit = ch - 'A' + 10; + else if (ch >= 'a' && ch <= 'z') + digit = ch - 'a' + 10; + else + return -1; + + if (digit >= base) + return -1; + + ret = ret * base + digit; + } /* for */ + + return ret; +} + + + + +/** + * Process strings, converting \n, \t, \v, \b, \r, \f, \\, + * \ddd, \xdd and \x0dd to actual chars. + * (Note: \x is an extension to support hexadecimal) + * This is used to allow the messages to use C escape sequences. + * Modifies the line in-place (always same size or shorter). + * Returns a pointer to input string. + */ + +char special[] = "\\nrtvbafx"; +char translated[]= "\\\n\r\t\v\b\a\fx"; + + +char * +processEscChars (char *line) +{ + /* used when converting \xdd and \ddd (hex or octal) characters */ + char ch; + char *src = line; + char *dst = line; /* possible as dst is shorter than src */ + char *s; + int chx; + + + if (line == NULL) + return line; + + /* cycle through copying characters, except when a \ is encountered. */ + while (*src != '\0') + { + ch = *src; + src++; + + if (ch != '\\') + { + *dst++ = ch; + continue; + } + + ch = *src; /* what follows slash? */ + src++; + + s = strchr(special, ch); /* is it a special character?? */ + + if (s == NULL) /* no. store the character as is */ + { + *dst++ = ch; + continue; + } + ch = translated[s - special]; + + if (ch != 'x') /* newline, formfeed etc. */ + { + *dst++ = ch; + continue; + } + + chx = mystrtoul (src, 16, 2); /* get value */ + if (chx >= 0) + { /* store character */ + *dst = chx; + dst++; + src += 2; + } + else /* error so just store x (loose slash) */ + { + *dst = *src; + dst++; + } + } /* while */ + + /* ensure '\0' terminated */ + *dst = '\0'; + + return line; +} + + +// +// process a single FIND.DE file +// + +int process_language_file(FILE *fd, char *filename) + { + FILE *fdres; + char linebuff[512]; + char msg[512]; + int id1, id2; + struct message_header message_header; + int len; + int linecount = 0; + + if ((fdres = fopen(filename, "r")) == 0) + { + printf("can't open %s\n", filename); + return 0; + } + + while (fgets(linebuff, sizeof(linebuff), fdres)) + { + char *s = linebuff; + + linecount++; + + for (len = strlen(s); len > 0; ) // delete trailing white space + { // from line + len--; + if (strchr(" \t\r\n", s[len]) == NULL) + break; + s[len] = '\0'; + } + if (len == 0) + continue; + + + if (linebuff[0] == '#') + { + // Skip comment line + } + else if (sscanf(linebuff, " %u . %u :%s", &id1, &id2, msg) != 3) + { + if (sscanf(linebuff, " %c", &id1) == 0 || // empty line + id1 == '#') // # comment line + continue; + + printf("%3u:%10.10s: missing 'num, num :' \n", linecount, linebuff); + continue; + } + s = strchr(linebuff, ':'); + if (s == NULL) continue; + + s++; + + processEscChars(s); + + if (strlen(s) > 250) + { + printf("%10.10s: lines must not be longer then 250 characters, is %u\n", linebuff, strlen(s)); + s[250] = 0; + } + + /* now here we have destilled id1, id2, message s */ + + message_header.len = strlen(s) // the message + + 1 // the trailing \0 + + sizeof(message_header); + message_header.id = (id1 << 8) + id2; + + fwrite(&message_header, 1, sizeof(message_header), fd); + fwrite(s, strlen(s)+1,1,fd); + + } + + + message_header.len = 0; // END message + message_header.id = 0; + + fwrite(&message_header, 1, sizeof(message_header), fd); + + return linecount; + } + + +// this is the 'compiler' part of KITTENC +// +// attach("FIND.EXE", "NLS\\FIND.??", 1) +// attach("FIND.EXE", "NLS\\FIND.DE", NLS\\FIND.ES", 2) +// + +int attach(char *progname, char *args[], int argc) + { + int i; + FILE *fd; + struct _finddata_t fileinfo; + long search_handle; + char *s; + char *filename; + char path_buffer[_MAX_PATH]; + struct content content[KITTEN_MAX_RESOURCES]; + struct message_end message_end; + int linecount; + + int resourcecount = 0; + + if ((fd = fopen(progname, "r+b")) == NULL) + { + printf(kittengets(2,1, "executable file <%s> can't be opened. because <%s>\n"), progname, strerror( errno )); + return 1; + } + fseek(fd, 0, SEEK_END); + + for (i = 0; i < argc; i++) + { + filename = args[i]; + +#ifdef __UNIX__ + /* no need to expand wildcards on unix */ + strcpy(path_buffer, filename); +#else + + if ((search_handle = _findfirst(args[i], &fileinfo)) == 0) + { + printf(kittengets(2,2,"%s: no such file\n"), args[i]); + return 1; + } + + do { /* the name is name only, without the path */ + + if ((s = strrchr(filename, '\\')) != NULL || + (s = strrchr(filename, '/')) != NULL || + (s = strrchr(filename, ':')) != NULL) + { + memcpy(path_buffer, filename, s-filename+1); + strcpy(path_buffer + (s-filename) +1, fileinfo.name); + } + else + strcpy(path_buffer, fileinfo.name); + +#endif + + printf("%s:", path_buffer); + + + if ((s = strrchr(path_buffer, '.')) == NULL || + strlen(path_buffer) - (s - path_buffer) != 3) + { + printf(kittengets(2,3,"file names MUST end with .EN\n")); + continue; + } + + if (resourcecount > KITTEN_MAX_RESOURCES) + { + printf(kittengets(2,4,"too many resourcefiles (max %d)\n"), KITTEN_MAX_RESOURCES); + continue; + } + + content[resourcecount].filepos_start = ftell(fd); + strcpy(content[resourcecount].language,s+1); + + + linecount = process_language_file(fd, path_buffer); + + content[resourcecount].filepos_end = ftell(fd); + content[resourcecount].reserved = 0; + + printf("%u messages, %lu byte\n", linecount, content[resourcecount].filepos_end - content[resourcecount].filepos_start); + + resourcecount++; + +#ifndef __UNIX__ + } while (_findnext(search_handle, &fileinfo) == 0); +#endif + } + + message_end.filepos = ftell(fd); + + /* make all absolute filepositions relative to file end + to make UPX support easier */ + + + fwrite(&content, sizeof(content[0]),resourcecount,fd); + + message_end.resource_count = resourcecount; + memcpy(message_end.ID, "KITTENC",8); + + message_end.fileend_orig = ftell(fd) + sizeof(message_end); + + fwrite(&message_end, sizeof(message_end),1,fd); + fclose(fd); + + return 0; + } + +enum { KITTEN_TRUNCATE, KITTEN_INFO, KITTEN_DUMP }; + +do_kitten_stuff(char *progname, int mode) + { + int fd; + struct message_end message_end; + struct content content[KITTEN_MAX_RESOURCES]; + int i; + long fileendnow, seekoffset; + + + + if ((fd = open(progname, _O_RDWR | _O_BINARY)) < 0) + { + printf("can't open %s because<%s>\n", progname, strerror(errno)); + return 0; + } + fileendnow = lseek(fd, 0, SEEK_END); + + lseek(fd, - (int)sizeof(struct message_end), SEEK_END); + + read(fd, &message_end, sizeof(struct message_end)); + + if (memcmp(message_end.ID, "KITTENC", 8) != 0) + { + printf("no KITTENC record found\n"); + goto RETURN; + } + + if (message_end.resource_count > KITTEN_MAX_RESOURCES) + { + printf("resource > %d\n", KITTEN_MAX_RESOURCES); + goto RETURN; + } + + seekoffset = fileendnow - message_end.fileend_orig; + + if (lseek(fd, message_end.filepos+seekoffset, SEEK_SET) != message_end.filepos+seekoffset) + { + printf("can't seek 1\n"); + goto RETURN; + } + + read(fd, &content, sizeof(content[0]) * message_end.resource_count); + + + if (mode == KITTEN_TRUNCATE) + { + _chsize(fd, content[0].filepos_start); // truncate file + goto RETURN; + } + if (mode == KITTEN_INFO) + { + printf("%s: %u languages supported\n", progname, message_end.resource_count); + + for (i = 0; i < message_end.resource_count; i++) + { + printf("%2.2s %lu byte\n", content[i].language, content[i].filepos_end - content[i].filepos_start); + } + + goto RETURN; + } + + if (mode == KITTEN_DUMP) + { + char resname[_MAX_PATH],*s; + char outname[_MAX_PATH]; + FILE *fdout; + int i; + int linecount; + + for (i = 0; i < message_end.resource_count; i++) // for each language + { + _splitpath(progname, NULL,NULL, resname, NULL); // reconstruct KITTENC.DE + + sprintf(outname, "%s.%c%c", resname, content[i].language[0], content[i].language[1]);; + + if ((fdout = fopen(outname, "w")) == NULL) // create KITTENC.DE + { + printf("can't write resourcefile %s\n", outname); + goto RETURN; + } + lseek(fd, content[i].filepos_start+seekoffset, SEEK_SET); + + printf("%s : ", outname); + + linecount= 0; + + while (1) + { + short len, id; char c; + + read(fd, &len, sizeof(short)); + + if (len == 0) + break; + + read(fd,&id, sizeof(short)); + + fprintf(fdout, "%u,%u:", id >> 8, id & 0xff); + + while (read(fd, &c, 1) == 1 && + c != 0) + { + if (c >= 0x20) + fputc(c, fdout); + else + { + if ((s = strchr(translated, c)) != NULL) /* \t \n \f */ + { + fprintf(fdout, "\\%c", special[s - translated]); + } + else + fprintf(fdout, "\\x%02x", c); + } + } // next character + + fprintf(fdout, "\n"); + linecount++; + } // next line + + printf("%u messages\n", linecount); + fclose(fdout); + } // next language + + + goto RETURN; + } + + + +RETURN: + close(fd); + return 0; + + } + + +main(int argc, char *argv[]) +{ + char *progname; + char buffer[_MAX_PATH]; + + kittenopen(argv[0]); + + printf("KITTENC 1.0\n"); + + //printf(kittengets(0,0,"hallo world\n")); + + if (argc < 3) + { + usage(); + exit(1); + } + + progname = argv[1]; +#ifndef __UNIX__ + strupr(progname); + + if (strstr(progname, ".EXE") == NULL && + strstr(progname, ".COM") == NULL) + { + sprintf(buffer,"%s.EXE", progname); + progname = buffer; + } +#endif + + if (stricmp(argv[2], "ATTACH") == 0) + { + do_kitten_stuff(progname, KITTEN_TRUNCATE); + return attach(progname, argv+3, argc-3); + + } + else if (stricmp(argv[2], "TRUNCATE") == 0) + { + return do_kitten_stuff(progname, KITTEN_TRUNCATE); + + + } + else if (stricmp(argv[2], "INFO") == 0) + { + return do_kitten_stuff(progname, KITTEN_INFO); + } + else if (stricmp(argv[2], "DUMP") == 0) + { + return do_kitten_stuff(progname, KITTEN_DUMP); + } + else { + printf("'%s' is wrong %s %s %s\n", argv[2], argv[0], argv[1], argv[2]); + usage(); + exit(1); + } + +} + + diff --git a/kittenc.h b/kittenc.h new file mode 100644 index 0000000..d42ec27 --- /dev/null +++ b/kittenc.h @@ -0,0 +1,44 @@ +/* + KITTENC - compile kitten files and attach this to + executables in a way that the kitten library retrieves + the language resources, in the same way that + catgets/kittengets ever did + +*/ +/* + This software is free software; free to use, + modify, pass to others, whatever + + use it at your own risk +*/ + +#ifndef KITTENC_H +#define KITTENC_H + +//internal stuff shared between kitten.c and kittenc.c +struct message_header +{ + short len; + short id; + // char message[]; +}; + +struct content +{ + char language[4]; + long filepos_start; // for this language + long filepos_end; + long reserved; // align on 16 byte to look better in hex editor +}; + +struct message_end +{ + long filepos; // points to content + long resource_count;// number of entries + long fileend_orig; // file end NOW because UPX + char ID[8]; // "KITTENC" +}; + +#define KITTEN_MAX_RESOURCES 16 + +#endif /* KITTENC_H */ @@ -2,7 +2,7 @@ # Assuming you have sourced `owsetenv` beforehand. # All binaries to build -bins = vbmouse.exe vbsf.exe vbmouse.drv +bins = vbmouse.exe vbsf.exe vbmouse.drv moustest.exe # Inf files infs = oemsetup.inf @@ -16,7 +16,7 @@ sfdos_objs = sftsr.obj sfmain.obj kitten.obj vbox.obj # Compiler arguments for DOS dos_cflags = -bt=dos -ms -6 -osi -w3 -wcd=202 -# -ms to use small memory model (though sometimes ss != ds...) +# -ms to use small memory model (this assumes ss == ds) # -osi to optimize for size, put intrinsics inline (to avoid runtime calls) # -w3 enables warnings # -wcd=202 disables the unreferenced function warning (e.g., for inline functions in headers) @@ -37,16 +37,25 @@ w16dll_cflags = -bt=windows -bd -mc -zu -s -6 -osi -w3 -wcd=202 compile_dos = *wcc -fo=$^@ $(dos_cflags) $[@ compile_dostsr = *wcc -fo=$^@ $(dostsr_cflags) $[@ compile_w16dll = *wcc -fo=$^@ $(w16dll_cflags) $[@ +compile_link_host = *wcl386 -i=$(%originclude) -os -fe=$^@ $[@ + +!ifdef __UNIX__ +run_host = ./ +!else +run_host = +!endif .BEFORE: # We need DOS and Windows headers, not host platform's + set originclude=$(%include) set include=$(%watcom)/h/win;$(%watcom)/h all: $(bins) .SYMBOLIC # DOS mouse driver -vbmouse.exe: vbmouse.lnk $(mousedos_objs) +vbmouse.exe: vbmouse.lnk $(mousedos_objs) kittenc.exe *wlink @$[@ name $@ file { $(mousedos_objs) } + $(run_host)kittenc.exe $@ attach nls/vbmouse.* mousetsr.obj: mousetsr.c .AUTODEPEND $(compile_dostsr) @@ -62,13 +71,14 @@ mousew16.obj: mousew16.c .AUTODEPEND $(compile_w16dll) # DOS shared folders -vbsf.exe: vbsf.lnk $(sfdos_objs) +vbsf.exe: vbsf.lnk $(sfdos_objs) kittenc.exe *wlink @$[@ name $@ file { $(sfdos_objs) } + $(run_host)kittenc.exe $@ attach nls/vbsf.* sftsr.obj: sftsr.c .AUTODEPEND $(compile_dostsr) -sfmain.obj: sfmain.c .AUTODEPEND +sfmain.obj: sfmain.c unitbl.h .AUTODEPEND $(compile_dos) # Auxiliary object files @@ -78,14 +88,35 @@ vbox.obj: vbox.c .AUTODEPEND kitten.obj: kitten.c .AUTODEPEND $(compile_dos) +# embed all unitbl/*.tbl files into a single unitbl.h +unitbl.h: unitbl2c.exe $(unitbls) + $(run_host)unitbl2c.exe $@ unitbl/*.tbl + +# Test programs +moustest_objs = moustest.obj + +moustest.exe: moustest.obj + *wlink system dos name $@ file { $(moustest_objs) } + +moustest.obj: moustest.c .AUTODEPEND + $(compile_dos) -mc -zu + +# Programs to build for the host +host_bins = unitbl2c.exe kittenc.exe +unitbl2c.exe: unitbl2c.c + $(compile_link_host) + +kittenc.exe: kittenc.c + $(compile_link_host) + # Other targets clean: .SYMBOLIC - rm -f vbmouse.exe vbmouse.drv vbsf.exe vbados.flp *.obj *.map + rm -f vbados.zip vbados.flp vbasrc.zip + rm -f $(bins) $(host_bins) + rm -f *.obj *.map vbados.flp: mformat -C -f 1440 -v VBADOS -i $^@ :: - mcopy -i $^@ nls/*.tbl :: - mcopy -i $^@ nls/vbsf.* nls/vbmouse.* :: # Build a floppy image containing the driver flp: vbados.flp $(bins) $(infs) .SYMBOLIC @@ -93,5 +124,7 @@ flp: vbados.flp $(bins) $(infs) .SYMBOLIC # Build a zip with the driver binaries zip: vbmouse.exe vbmouse.drv oemsetup.inf vbsf.exe .SYMBOLIC - zip --DOS-names -fz- -j vbados.zip nls/*.tbl nls/vbsf.* nls/vbmouse.* zip --DOS-names -fz- vbados.zip $(bins) $(infs) + +srczip: .SYMBOLIC + zip --DOS-names -fz- vbasrc.zip COPYING README.md makefile *.lnk *.h *.c *.inf nls/* unitbl/* doc/* @@ -27,6 +27,9 @@ #include "int16kbd.h" #include "int2fwin.h" #include "int33.h" +#include "pic.h" +#include "serial.h" +#include "sermouse.h" #include "vbox.h" #include "vmware.h" #include "mousetsr.h" @@ -35,6 +38,8 @@ TSRDATA data; +static const char version_string[] = "VBADOS"; + static const uint16_t default_cursor_graphic[] = { 0x3FFF, 0x1FFF, 0x0FFF, 0x07FF, 0x03FF, 0x01FF, 0x00FF, 0x007F, @@ -537,6 +542,8 @@ static void refresh_video_info(void) reload_video_info(); + // This is one of these compatibility-delicate things + // TODO: Investigate correct behavior here if (data.video_mode.type != VIDEO_UNKNOWN) { // If we know the screen size for this mode, then reset the window to it data.min.x = 0; @@ -547,43 +554,52 @@ static void refresh_video_info(void) } } -/** Calls the application-registered event handler. */ -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) +/** Obtains INT33_EVENT_MASK bitmask corresponding to btn. */ +static uint16_t button_event_mask(int btn, bool released) { -#if TRACE_EVENTS - dprintf("calling event handler events=0x%x buttons=0x%x x=%d y=%d dx=%d dy=%d\n", - events, buttons, x, y, delta_x, delta_y); -#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] + unsigned int idx; + if (btn >= 3) { + idx = INT33_EVENT_MASK_4TH_BUTTON_PRESSED_INDEX; + btn -= 3; + } else { + idx = INT33_EVENT_MASK_LEFT_BUTTON_PRESSED_INDEX; } + idx += (btn * 2); + if (released) idx++; + return 1 << idx; } +/** Calls the application-registered event handler. */ +static void call_event_handler(uint16_t events, uint16_t buttons, + int16_t x, int16_t y, + int16_t delta_x, int16_t delta_y, + void (__far *handler)()); +#pragma aux call_event_handler = \ + "push bp" \ + "mov bp, sp" \ + "push ds" \ + "call dword ptr 0x2[bp]" \ + "pop ds" \ + "pop bp" \ + __parm __caller [ax] [bx] [cx] [dx] [si] [di] [] \ + __modify [es fs gs] + /** Process a mouse event internally. * @param buttons currently pressed buttons as a bitfield * @param absolute whether mouse coordinates are an absolute value * @param x y if absolute, then absolute coordinates in screen pixels * if relative, then relative coordinates in mickeys - * @param z relative wheel mouse movement + * @param wheeln wheel number (0 = vertical, 1 = horizontal) + * @param z delta movement reported for that wheel (or 0) */ -static void handle_mouse_event(uint16_t buttons, bool absolute, int x, int y, int z) +static void handle_mouse_event(uint16_t buttons, bool absolute, int x, int y, char wheeln, int z) { uint16_t events = 0; int i; #if TRACE_EVENTS - dprintf("handle mouse event %s buttons=0x%x x=%d y=%d z=%d\n", - absolute ? "absolute" : "relative", buttons, x, y, z); + dprintf("handle mouse event %s buttons=0x%hx x=%d y=%d z%d=%d\n", + absolute ? "absolute" : "relative", buttons, x, y, wheeln, z); #endif if (absolute) { @@ -647,43 +663,44 @@ static void handle_mouse_event(uint16_t buttons, bool absolute, int x, int y, in bound_position_to_window(); #if USE_WHEEL - if (data.haswheel && z) { - if (!data.usewheelapi && (data.wheel_up_key || data.wheel_down_key)) { + if (data.num_wheels && z) { + if (!data.usewheelapi && (data.wheel_key[wheeln][WHEEL_DIR_UP] || data.wheel_key[wheeln][WHEEL_DIR_DOWN])) { // Emulate keystrokes on wheel movement - if (z < 0 && data.wheel_up_key) { + if (z < 0 && data.wheel_key[wheeln][WHEEL_DIR_UP]) { for (; z < 0; z++) { - int16_store_keystroke(data.wheel_up_key); + int16_store_keystroke(data.wheel_key[wheeln][WHEEL_DIR_UP], 0); } - } else if (z > 0 && data.wheel_down_key) { + } else if (z > 0 && data.wheel_key[wheeln][WHEEL_DIR_DOWN]) { for (; z > 0; z--) { - int16_store_keystroke(data.wheel_down_key); + int16_store_keystroke(data.wheel_key[wheeln][WHEEL_DIR_DOWN], 0); } } } else { - events |= INT33_EVENT_MASK_WHEEL_MOVEMENT; + if (wheeln == 1) events |= INT33_EVENT_MASK_HORIZ_WHEEL_MOVEMENT; + else events |= INT33_EVENT_MASK_WHEEL_MOVEMENT; // Higher byte of buttons contains wheel movement buttons |= (z & 0xFF) << 8; // Accumulate delta wheel movement - data.wheel_delta += z; - data.wheel_last.x = data.pos.x; - data.wheel_last.y = data.pos.y; + data.wheel[wheeln].delta += z; + data.wheel[wheeln].last.x = data.pos.x; + data.wheel[wheeln].last.y = data.pos.y; } } #endif // Update button status - for (i = 0; i < NUM_BUTTONS; ++i) { + for (i = 0; i < data.num_buttons; ++i) { uint8_t btn = 1 << i; - uint8_t evt = 0; + uint16_t evt = 0; if ((buttons & btn) && !(data.buttons & btn)) { // Button pressed - evt = 1 << (1 + (i * 2)); // Press event mask + evt = button_event_mask(i, false); 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 + evt = button_event_mask(i, true); data.button[i].released.count++; data.button[i].released.last.x = data.pos.x; data.button[i].released.last.y = data.pos.y; @@ -694,41 +711,90 @@ static void handle_mouse_event(uint16_t buttons, bool absolute, int x, int y, in refresh_cursor(); - events &= data.event_mask; - if (data.event_handler && events) { - x = snap_to_grid(data.pos.x, data.screen_granularity.x); - y = snap_to_grid(data.pos.y, data.screen_granularity.y); + if (!data.event_handler) { + // No event handler + return; + } - call_event_handler(data.event_handler, events, - buttons, x, y, data.delta.x, data.delta.y); + events &= data.event_mask; + if (!(events & ~INT33_EVENT_MASK_ABSOLUTE)) { + // No event passes the mask + return; } + + x = snap_to_grid(data.pos.x, data.screen_granularity.x); + y = snap_to_grid(data.pos.y, data.screen_granularity.y); + +#if TRACE_EVENTS + dprintf("calling user event handler events=0x%x buttons=0x%x x=%d y=%d dx=%d dy=%d\n", + events, buttons, x, y, data.delta.x, data.delta.y); +#endif + + call_event_handler(events, buttons, x, y, data.delta.x, data.delta.y, + data.event_handler); + +#if TRACE_EVENTS + dputs("return from user event handler"); +#endif } static void handle_ps2_packet(void) { - unsigned status; - int x, y, z = 0; - bool abs = false; + unsigned status = data.cur_packet[0]; - // Decode the PS2 packet... - status = data.ps2_packet[0]; - x = data.ps2_packet[1]; - y = data.ps2_packet[2]; + // Decode basic PS/2 packet + unsigned buttons = status & (PS2M_STATUS_BUTTON_1 | PS2M_STATUS_BUTTON_2 | PS2M_STATUS_BUTTON_3); + int x = data.cur_packet[1], y = data.cur_packet[2]; -#if USE_WHEEL - if (data.haswheel) { - // Sign-extend Z - z = (int8_t) data.ps2_packet[3]; - } -#endif + // For the extended byte... + bool abs = false; + int z = 0; + char wheeln = 0; // Sign-extend X, Y as per the status byte x = status & PS2M_STATUS_X_NEG ? 0xFF00 | x : x; y = -(status & PS2M_STATUS_Y_NEG ? 0xFF00 | y : y); + // Decode extended byte +#if USE_WHEEL + switch (data.device_id) { + case PS2M_DEVICE_ID_IMPS2: + // ImPS/2. The fourth packet is the (vertical) wheel movement. + z = (int8_t) data.cur_packet[3]; + break; +#if USE_IMEX + case PS2M_DEVICE_ID_IMEX: + case PS2M_DEVICE_ID_IMEX_HORZ: + // IntelliMouse Explorer. It can report either: + if (data.cur_packet[3] & PS2M_IMEX_VERTICAL_SCROLL) { + // Vertical scrolling, with 6 bits of precision. + z = sign_extend(data.cur_packet[3], 6); + // Assume 4th/5th buttons are still pressed if they were + buttons |= data.buttons & (INT33_BUTTON_MASK_4TH|INT33_BUTTON_MASK_5TH); + } else if (data.cur_packet[3] & PS2M_IMEX_HORIZONTAL_SCROLL) { + // Horizontal scrolling, with 6 bits of precision. + z = sign_extend(data.cur_packet[3], 6); + wheeln = 1; + buttons |= data.buttons & (INT33_BUTTON_MASK_4TH|INT33_BUTTON_MASK_5TH); + } else { + // Or 2 extra buttons (4, 5) + if (data.cur_packet[3] & PS2M_IMEX_BUTTON_4) { + buttons |= INT33_BUTTON_MASK_4TH; + } + if (data.cur_packet[3] & PS2M_IMEX_BUTTON_5) { + buttons |= INT33_BUTTON_MASK_5TH; + } + // Plus (vertical) scrolling with 4 bits of precision. + z = sign_extend(data.cur_packet[3], 4); + } + break; +#endif /* USE_IMEX */ + } +#endif /* USE_WHEEL */ + #if TRACE_PROTO - dprintf("ps2 packet %x %d %d %d\n", status, x, y, z); -#endif /* TRACE_EVENTS */ + dprintf("ps2 decoded packet buttons=%x x=%d y=%d z%d=%d\n", buttons, x, y, wheeln, z); +#endif /* TRACE_PROTO */ #if USE_VIRTUALBOX if (data.vbavail) { @@ -781,16 +847,17 @@ static void handle_ps2_packet(void) y = scaleu(vmw.y & 0xFFFFU, 0xFFFFU, MAX(data.max.y, data.screen_max.y)); z = (uint8_t) vmw.z; + wheeln = 0; // VMware only supports wheel 0 } if (vmw.status & VMWARE_ABSPOINTER_STATUS_BUTTON_LEFT) { - status |= PS2M_STATUS_BUTTON_1; + buttons |= INT33_BUTTON_MASK_LEFT; } if (vmw.status & VMWARE_ABSPOINTER_STATUS_BUTTON_RIGHT) { - status |= PS2M_STATUS_BUTTON_2; + buttons |= INT33_BUTTON_MASK_RIGHT; } if (vmw.status & VMWARE_ABSPOINTER_STATUS_BUTTON_MIDDLE) { - status |= PS2M_STATUS_BUTTON_3; + buttons |= INT33_BUTTON_MASK_CENTER; } } else { return; // Ignore the PS/2 packet otherwise, it is likely garbage @@ -798,8 +865,7 @@ static void handle_ps2_packet(void) } #endif /* USE_VMWARE */ - handle_mouse_event(status & (PS2M_STATUS_BUTTON_1 | PS2M_STATUS_BUTTON_2 | PS2M_STATUS_BUTTON_3), - abs, x, y, z); + handle_mouse_event(buttons, abs, x, y, wheeln, z); } /** PS/2 BIOS calls this routine to notify mouse events. @@ -811,11 +877,11 @@ static void ps2_mouse_handler(uint16_t word0, uint16_t word1, uint16_t word2, ui uint16_t ticks = bda_get_tick_count_lo(); // Are we using the BIOS in 3-packet mode directly? - if (data.bios_packet_size == PS2M_PACKET_SIZE_PLAIN) { + if (data.bios_packet_size == PS2M_PACKET_SIZE_STD) { // Just forward it to the full packet handler. - data.ps2_packet[0] = word0; - data.ps2_packet[1] = word1; - data.ps2_packet[2] = word2; + data.cur_packet[0] = word0; + data.cur_packet[1] = word1; + data.cur_packet[2] = word2; (void) word3; handle_ps2_packet(); return; @@ -825,23 +891,23 @@ static void ps2_mouse_handler(uint16_t word0, uint16_t word1, uint16_t word2, ui // receiving one byte at a time. // We have to compute synchronization ourselves. -#if TRACE_PROTO - dprintf("ps2 callback byte %d/%d = %x\n", - 1 + data.cur_packet_bytes, data.packet_size, word0 & 0xFF); -#endif /* TRACE_EVENTS */ - if (data.cur_packet_bytes && - ticks >= data.cur_packet_ticks + MAX_PS2_PACKET_DELAY) { + ticks >= data.cur_packet_ticks + MAX_PACKET_DELAY) { // Assume the start of a new packet - dprintf("dropping packet! prev_ticks=%u new_ticks=%u\n", - data.cur_packet_ticks, ticks); + dprintf("dropping packet! cur_bytes=%u prev_ticks=%u new_ticks=%u\n", + data.cur_packet_bytes, data.cur_packet_ticks, ticks); data.cur_packet_bytes = 0; } if (data.cur_packet_bytes == 0) { data.cur_packet_ticks = ticks; } - data.ps2_packet[data.cur_packet_bytes] = word0; +#if TRACE_PROTO + dprintf("ps2 callback byte %d/%d = %hx\n", + 1 + data.cur_packet_bytes, data.packet_size, word0 & 0xFF); +#endif /* TRACE_PROTO */ + + data.cur_packet[data.cur_packet_bytes] = word0; data.cur_packet_bytes++; if (data.cur_packet_bytes >= data.packet_size) { @@ -922,28 +988,41 @@ static void set_absolute(bool enable) } #endif /* USE_INTEGRATION */ -static void reset_mouse_hardware() +static bool reset_ps2_mouse() { int err; // Stop receiving bytes... ps2m_enable(false); + data.port = 0; data.bios_packet_size = PS2M_PACKET_SIZE_STREAMING; // Default to use the BIOS in streaming mode - data.packet_size = PS2M_PACKET_SIZE_PLAIN; + data.packet_size = PS2M_PACKET_SIZE_STD; data.cur_packet_bytes = 0; data.cur_packet_ticks = 0; + data.num_buttons = MIN(3, MAX_BUTTONS); +#if USE_WHEEL + data.num_wheels = 0; + data.usewheelapi = 0; +#endif + + err = ps2m_get_device_id(&data.device_id); + if (err) data.device_id = PS2M_DEVICE_ID_STD; #if USE_WIN386 if (data.haswin386) { uint8_t device_id; - // Normally, win386 does not support anything except PS2M_PACKET_SIZE_PLAIN - // However, if we detect our special wheelvkd driver is running... - err = ps2m_get_device_id(&device_id); - if (err || device_id != PS2M_DEVICE_ID_IMPS2) { - // Our special driver is not running... - dputs("Windows running, using plain packet size"); - data.bios_packet_size = PS2M_PACKET_SIZE_PLAIN; + // Normally, win386 does not support anything except standard mouse type + // and standard packet size. We should not try setting up the BIOS + // in streaming mode as some versions of Windows will silently fail to do so. + + // However, our special wheelvkd will allow us to use streaming mode. + // wheelvkd signals it is running by using a the imex device IDs even + // before the knocking sequence: + if (data.device_id == PS2M_DEVICE_ID_STD) { + // Our special driver is NOT running... + dputs("Windows running, using standard packet size"); + data.bios_packet_size = PS2M_PACKET_SIZE_STD; } } #endif /* USE_WIN386 */ @@ -951,33 +1030,61 @@ static void reset_mouse_hardware() // Try to init PS/2 BIOS with desired packet size / streaming mode err = ps2m_init(data.bios_packet_size); - if (err && data.bios_packet_size != PS2M_PACKET_SIZE_PLAIN) { - // However, if there is an error, drop down to plain packet size + if (err && data.bios_packet_size != PS2M_PACKET_SIZE_STD) { + // However, if there is an error, drop down to std packet size // Emulators like DOSBox don't support anything but plain packet size - dputs("BIOS didn't support streaming mode, using plain packet size"); - data.bios_packet_size = PS2M_PACKET_SIZE_PLAIN; + dputs("BIOS doesn't support streaming mode, using standard packet size"); + data.bios_packet_size = PS2M_PACKET_SIZE_STD; err = ps2m_init(data.bios_packet_size); } if (err) { - dputs("error on ps2m_init during reset, ignoring"); + dputs("error on ps2m_init during reset"); + return false; } #if USE_WHEEL if (data.usewheel && data.bios_packet_size == PS2M_PACKET_SIZE_STREAMING - && ps2m_detect_wheel()) { - dputs("PS/2 wheel detected"); - data.haswheel = true; + && ps2m_detect_imps2()) { + dputs("ImPS/2 detected"); data.packet_size = PS2M_PACKET_SIZE_EXT; + + data.num_wheels = MIN(1, MAX_WHEELS); + ps2m_get_device_id(&data.device_id); + +#if USE_IMEX + // Try to go a bit further + if (data.device_id == PS2M_DEVICE_ID_IMPS2) { + ps2m_send_imex_sequence(); + + ps2m_get_device_id(&data.device_id); + if (data.device_id == PS2M_DEVICE_ID_IMEX) { + ps2m_send_imex_horz_sequence(); + + ps2m_get_device_id(&data.device_id); + // According to VirtualBox device ID will not change after this + // sequence. We don't need to check for it anyway as our code to + // handle both protocols is the same. + } + } + + if (data.device_id == PS2M_DEVICE_ID_IMEX + || data.device_id == PS2M_DEVICE_ID_IMEX_HORZ) { + data.num_wheels = MIN(2, MAX_WHEELS); + data.num_buttons = MIN(5, MAX_BUTTONS); + } +#endif /* USE_IMEX */ + + dprintf("found mouse device id = 0x%hx num buttons=%d num wheels=%d\n", + data.device_id, data.num_buttons, data.num_wheels); } else { if (data.usewheel) dputs("PS/2 wheel NOT detected"); - data.haswheel = false; } #if USE_VMWARE // With the VMware backdoor, we can get the wheel information even if // we couldn't configure the PS/2 mouse at all. if (data.vmwavail && data.usewheel) { - data.haswheel = true; + data.num_wheels = 1; } #endif /* USE_VMWARE */ #endif /* USE_WHEEL */ @@ -996,6 +1103,56 @@ static void reset_mouse_hardware() #endif ps2m_enable(true); + return true; +} + +#if USE_SERIAL +static bool reset_serial_mouse() +{ + // TODO: Currently this is not power-cycling nor redetecting the mouse + unsigned iobase = data.port_io; + + data.cur_packet_bytes = 0; + data.cur_packet_ticks = 0; + + while (serial_data_ready(iobase)) { + uint8_t byte = serial_read_data(iobase); + dprintf("got byte from serial %hx '%c'\n", byte, byte); + } + + // First, reconfigure the serial port to our liking + serial_configure_line(iobase, SERIAL_DIVISOR_1200, SERIAL_LCR_WL_7|SERIAL_LCR_PAR_NONE); + // Keep the mouse powered on + serial_set_modem_control(iobase, SERIAL_MCR_DTR | SERIAL_MCR_RTS); + // Reset and enable the FIFO + serial_set_fifo_control(iobase, SERIAL_FCR_FIFO_ENABLE|SERIAL_FCR_FIFO_RESET); + // And finally enable interrupts + serial_configure_interrupts(iobase, SERIAL_IER_ERBFI); + pic_unmask_irq(data.port_irq); + + dputs("Serial mouse reset done"); + + return true; +} +#endif + +static bool reset_mouse_hardware() +{ + dputs("Reset mouse hardware"); + + // I don't want to search for mouse again. + // So this won't switch from serial to PS/2. + // For that, reload the mouse driver. + + if (data.port == 0) { + return reset_ps2_mouse(); + } else { +#if USE_SERIAL + return reset_serial_mouse(); +#else + return false; +#endif + } } /** Reset "software" mouse settings, i.e. those configurable by the client program. */ @@ -1019,10 +1176,6 @@ static void reset_mouse_settings() data.cursor_hotspot.y = 0; memcpy(data.cursor_graphic, default_cursor_graphic, sizeof(data.cursor_graphic)); -#if USE_WHEEL - data.usewheelapi = false; -#endif - refresh_cursor(); // This will hide the cursor and update data.cursor_visible } @@ -1030,8 +1183,8 @@ static void reset_mouse_settings() static void reset_mouse_state() { int i; - data.pos.x = data.min.x; - data.pos.y = data.min.y; + data.pos.x = data.min.x + (data.max.x - data.min.x) / 2; + data.pos.y = data.min.y + (data.max.y - data.min.y) / 2; data.pos_frac.x = 0; data.pos_frac.y = 0; data.delta.x = 0; @@ -1041,7 +1194,7 @@ static void reset_mouse_state() data.abs_pos.x = -1; data.abs_pos.y = -1; data.buttons = 0; - for (i = 0; i < NUM_BUTTONS; i++) { + for (i = 0; i < MAX_BUTTONS; i++) { data.button[i].pressed.count = 0; data.button[i].pressed.last.x = 0; data.button[i].pressed.last.y = 0; @@ -1049,7 +1202,13 @@ static void reset_mouse_state() data.button[i].released.last.x = 0; data.button[i].released.last.y = 0; } - data.wheel_delta = 0; +#if USE_WHEEL + for (i = 0; i < MAX_WHEELS; i++) { + data.wheel[i].delta = 0; + data.wheel[i].last.x = 0; + data.wheel[i].last.y = 0; + } +#endif data.cursor_visible = false; data.cursor_pos.x = 0; data.cursor_pos.y = 0; @@ -1057,14 +1216,16 @@ static void reset_mouse_state() memset(data.cursor_prev_graphic, 0, sizeof(data.cursor_prev_graphic)); } +#if USE_WHEEL /** Return (in the appropiate registers) the wheel movement counter and afterwards reset it. */ -static void return_clear_wheel_counter(union INTPACK __far *r) +static void return_clear_wheel_counter(union INTPACK __far *r, struct wheelcounter *c) { - r->w.cx = snap_to_grid(data.wheel_last.x, data.screen_granularity.x); - r->w.dx = snap_to_grid(data.wheel_last.y, data.screen_granularity.y); - r->w.bx = data.wheel_delta; - data.wheel_delta = 0; + r->w.cx = snap_to_grid(c->last.x, data.screen_granularity.x); + r->w.dx = snap_to_grid(c->last.y, data.screen_granularity.y); + r->w.bx = c->delta; + c->delta = 0; } +#endif /** Return (in the appropiate registers) the desired button press counter and afterwards reset it. */ static void return_clear_button_counter(union INTPACK __far *r, struct buttoncounter *c) @@ -1098,10 +1259,14 @@ static void int33_handler(union INTPACK r) dputs("Mouse reset"); reload_video_info(); reset_mouse_settings(); - reset_mouse_hardware(); reset_mouse_state(); - r.w.ax = INT33_MOUSE_FOUND; - r.w.bx = NUM_BUTTONS; + if (reset_mouse_hardware()) { + r.w.ax = INT33_MOUSE_FOUND; + r.w.bx = data.num_buttons; + } else { + r.w.ax = 0; + r.w.bx = 0; + } break; case INT33_SHOW_CURSOR: if (data.hidden_count > 0) data.hidden_count--; @@ -1125,9 +1290,13 @@ static void int33_handler(union INTPACK r) r.w.dx = snap_to_grid(data.pos.y, data.screen_granularity.y); r.w.bx = data.buttons; #if USE_WHEEL - if (data.haswheel) { - r.h.bh = data.wheel_delta; - data.wheel_delta = 0; + if (data.num_wheels > 0) { + r.h.bh = data.wheel[0].delta; + data.wheel[0].delta = 0; + if (data.num_wheels > 1) { + r.h.ah = data.wheel[1].delta; + data.wheel[1].delta = 0; + } } #endif break; @@ -1151,18 +1320,19 @@ static void int33_handler(union INTPACK r) #endif r.w.ax = data.buttons; #if USE_WHEEL - if (data.haswheel) { - r.h.bh = data.wheel_delta; - if (r.w.bx == -1) { + if (data.num_wheels > 0) { + int n = -(int16_t)(r.w.bx); + r.h.ah = data.wheel[0].delta; + if (n >= 0 && n < MAX_WHEELS) { // Asked for wheel information - return_clear_wheel_counter(&r); + return_clear_wheel_counter(&r, &data.wheel[n]); break; } } #endif // Regular button information return_clear_button_counter(&r, - &data.button[MIN(r.w.bx, NUM_BUTTONS - 1)].pressed); + &data.button[MIN(r.w.bx, MAX_BUTTONS - 1)].pressed); break; case INT33_GET_BUTTON_RELEASED_COUNTER: #if TRACE_CALLS @@ -1170,17 +1340,18 @@ static void int33_handler(union INTPACK r) #endif r.w.ax = data.buttons; #if USE_WHEEL - if (data.haswheel) { - r.h.bh = data.wheel_delta; - if (r.w.bx == -1) { + if (data.num_wheels > 0) { + int n = -(int16_t)(r.w.bx); + r.h.ah = data.wheel[0].delta; + if (n >= 0 && n < MAX_WHEELS) { // Asked for wheel information - return_clear_wheel_counter(&r); + return_clear_wheel_counter(&r, &data.wheel[n]); break; } } #endif return_clear_button_counter(&r, - &data.button[MIN(r.w.bx, NUM_BUTTONS - 1)].released); + &data.button[MIN(r.w.bx, MAX_BUTTONS - 1)].released); break; case INT33_SET_HORIZONTAL_WINDOW: dprintf("Mouse set horizontal window [%d,%d]\n", r.w.cx, r.w.dx); @@ -1224,7 +1395,7 @@ static void int33_handler(union INTPACK r) data.delta.y = 0; break; case INT33_SET_EVENT_HANDLER: - dputs("Mouse set event handler"); + dprintf("Mouse set event handler mask=0x%x\n", r.w.cx); data.event_mask = r.w.cx; data.event_handler = MK_FP(r.w.es, r.w.dx); break; @@ -1281,13 +1452,16 @@ static void int33_handler(union INTPACK r) dputs("Mouse reset settings"); reload_video_info(); reset_mouse_settings(); + reset_mouse_state(); if (!data.bios_packet_size || !data.packet_size) { // Someone is calling this without calling reset first - reset_mouse_hardware(); + if (!reset_mouse_hardware()) { + r.w.ax = 0; + break; + } } - reset_mouse_state(); r.w.ax = INT33_MOUSE_FOUND; - r.w.bx = NUM_BUTTONS; + r.w.bx = data.num_buttons; break; case INT33_GET_LANGUAGE: r.w.bx = 0; @@ -1296,8 +1470,10 @@ static void int33_handler(union INTPACK r) dputs("Mouse get driver info"); r.h.bh = REPORTED_VERSION_MAJOR; r.h.bl = REPORTED_VERSION_MINOR; - r.h.ch = INT33_MOUSE_TYPE_PS2; - r.h.cl = 0; + // TODO Seems that windows may use this, albeit only when the following has MSB set? + r.h.ch = data.port == 0 ? INT33_MOUSE_TYPE_PS2 : INT33_MOUSE_TYPE_SERIAL; + r.h.cl = data.port_irq; + r.h.al = data.port; break; case INT33_GET_MAX_COORDINATES: r.w.bx = 0; @@ -1310,14 +1486,26 @@ static void int33_handler(union INTPACK r) r.w.cx = data.max.x; r.w.dx = data.max.y; break; + case INT33_GET_VERSION_STRING: + dputs("Mouse get version string"); + r.x.es = FP_SEG(&version_string); + r.x.di = FP_OFF(&version_string); + break; #if USE_WHEEL // Wheel API extensions: case INT33_GET_CAPABILITIES: dputs("Mouse get capabitilies"); r.w.ax = INT33_WHEEL_API_MAGIC; // Driver supports wheel API r.w.bx = 0; - r.w.cx = data.haswheel ? INT33_CAPABILITY_MOUSE_API : 0; - data.usewheelapi = true; // Someone calling this function likely wants to use wheel API + r.w.cx = 0; + if (data.num_wheels > 0) { + r.w.cx |= INT33_CAPABILITY_WHEEL_API; + if (data.num_wheels > 1) { + r.w.cx |= INT33_CAPABILITY_WHEEL2_API; + } + } + dprintf(" Returning capabilities=0x%x\n", r.w.cx); + data.usewheelapi = true; // Someone calling this function wants to use wheel API break; #endif // Our internal API extensions: @@ -1357,6 +1545,169 @@ void __declspec(naked) __far int33_isr(void) } } +#if USE_SERIAL +static void handle_serial_m_packet() +{ + int x = (int8_t) ((data.cur_packet[1] & SERMOUSE_MS_X_LO_MASK) + | ((data.cur_packet[0] & SERMOUSE_MS_X_HI_MASK) << SERMOUSE_MS_X_HI_SHIFT)); + int y = (int8_t) ((data.cur_packet[2] & SERMOUSE_MS_Y_LO_MASK) + | ((data.cur_packet[0] & SERMOUSE_MS_Y_HI_MASK) << SERMOUSE_MS_Y_HI_SHIFT)); + unsigned buttons = 0; + bool abs = false; + char wheeln = 0; + int z = 0; + + if (data.cur_packet[0] & SERMOUSE_MS_BUTTON_LEFT) + buttons |= INT33_BUTTON_MASK_LEFT; + if (data.cur_packet[0] & SERMOUSE_MS_BUTTON_RIGHT) + buttons |= INT33_BUTTON_MASK_RIGHT; + if (data.num_buttons >= 3 && (data.buttons & INT33_BUTTON_MASK_CENTER)) + buttons |= INT33_BUTTON_MASK_CENTER; // preserve status of mid button for now + +#if TRACE_PROTO + dprintf("serial decoded M packet buttons=%x x=%d y=%d\n", buttons, x, y); +#endif /* TRACE_PROTO */ + + handle_mouse_event(buttons, abs, x, y, wheeln, z); +} + +static void handle_serial_m3_packet() +{ + // No need to send mouse movement as it will have been processed + // back when we received the 3rd byte. + // Instead focus on wheel and mid mouse button which come in the 4rd byte. + int x = 0; + int y = 0; + // Preserve old button state for most part + unsigned buttons = data.buttons & (INT33_BUTTON_MASK_LEFT|INT33_BUTTON_MASK_RIGHT); + bool abs = false; + char wheeln = 0; // only one wheel supported for now + int z = sign_extend(data.cur_packet[3] & SERMOUSE_MS_Z_LO_MASK, 4); + + if (data.cur_packet[3] & SERMOUSE_MS_BUTTON_CENTER) { + buttons |= INT33_BUTTON_MASK_CENTER; + } + +#if TRACE_PROTO + dprintf("serial decoded M3 packet buttons=%x z=%d\n", buttons, z); +#endif /* TRACE_PROTO */ + + // Only send 2nd event if really necessary + if (buttons != data.buttons || z) { + handle_mouse_event(buttons, abs, x, y, wheeln, z); + } +} + +static void handle_serial_data(uint8_t byte) +{ + // TODO: not checking cur_packet_ticks for now, don't think it necessary for serial + // due to different synchronization + // Depending on the serial protocol now... + if (data.device_id == SERMOUSE_DEVICE_ID_MS) { + // All these protocols use bit #6 (MSB since we use 7 data bits) as Start-of-Frame indicator + if (byte & (1 << 6)) { + if (data.buttons & INT33_BUTTON_MASK_CENTER && data.cur_packet_bytes == 3) { + // Was the previous packet a 3byte one and the middle button is down? + // Assume the mouse just skipped the 4th byte and release the middle button + // TODO I am not sure if this hack is required +#if TRACE_PROTO + dputs("serial releasing mid button"); +#endif /* TRACE_PROTO */ + handle_mouse_event(data.buttons & ~INT33_BUTTON_MASK_CENTER, false, 0, 0, 0, 0); + } + + // Prepare for new packet + data.cur_packet_bytes = 0; + } + +#if TRACE_PROTO + dprintf("serial byte %d/%d = %hx\n", + 1 + data.cur_packet_bytes, data.packet_size, byte); +#endif /* TRACE_PROTO */ + + data.cur_packet[data.cur_packet_bytes] = byte; + data.cur_packet_bytes++; + + // However there is no End-of-Frame indicator. + // We do not know if this is going to be a 3 byte or 4 byte packet + // (apparently 4-byte packet mouses may send up a 3-byte packet + // if no wheel or extra buttons are modified) + // So we will send a "main" mouse event when we receive the 3rd byte + // and then follow up with a second mouse event if we receive the 4rd. + + if (data.cur_packet_bytes == 3) { + // send main mouse event now + handle_serial_m_packet(); + } else if (data.cur_packet_bytes == 4) { + // handle the 4th byte + handle_serial_m3_packet(); + } + + if (data.cur_packet_bytes >= data.packet_size) { + data.cur_packet_bytes = 0; + } + } +} + +static void irq3_4_handler() +#pragma aux irq3_4_handler "*" parm caller [] modify [ax bx cx dx es] +{ + uint16_t iobase = data.port_io; + uint8_t intid, byte; + + pic_eoi_irq(data.port_irq); + + while ((intid = serial_read_pending_interrupt(iobase)) != SERIAL_IID_NONE) { + switch (intid) { + case SERIAL_IID_ERROR: + byte = serial_get_line_status(iobase); + // If there is an error, we should likely clear our packet buffer + if (byte & SERIAL_LSR_OE | SERIAL_LSR_PE | SERIAL_LSR_FE) { + dputs("serial rx error"); + data.cur_packet_bytes = 0; + } + break; + case SERIAL_IID_DATA: + case SERIAL_IID_STALE: + while (serial_data_ready(iobase)) { + byte = serial_read_data(iobase); + handle_serial_data(byte); + } + break; + case SERIAL_IID_MODEM: + byte = serial_get_modem_status(iobase); // Read but ignore + break; + } + } +} + +void __declspec(naked) __far irq3_4_isr(void) +{ + __asm { + pusha + push ds + push es + push fs + push gs + + mov bp, sp + push cs + pop ds + + call irq3_4_handler + + pop gs + pop fs + pop es + pop ds + popa + + ; We don't chain to the previous handler + iret + } +} +#endif + #if USE_WIN386 /** Windows will call this function to notify events when we are inside a DOS box. */ static void windows_mouse_handler(int action, int x, int y, int buttons, int events) @@ -1366,7 +1717,7 @@ static void windows_mouse_handler(int action, int x, int y, int buttons, int eve case VMD_ACTION_MOUSE_EVENT: (void) events; // Forward event to our internal system - handle_mouse_event(buttons, true, x, y, 0); + handle_mouse_event(buttons, true, x, y, 0, 0); break; case VMD_ACTION_HIDE_CURSOR: dputs("VMD_ACTION_HIDE_CURSOR"); @@ -1472,7 +1823,7 @@ void __declspec(naked) __far int2f_isr(void) popa ; Jump to the next handler in the chain - jmp dword ptr cs:[data + 4] ; wasm doesn't support structs, this is data.prev_int2f_handler + jmp dword ptr cs:[data + (4*2)] ; wasm doesn't support structs, this is data.prev_int2f_handler } } #endif @@ -1490,7 +1841,13 @@ static LPTSRDATA int33_get_tsr_data(void); LPTSRDATA __far get_tsr_data(bool installed) { if (installed) { - return int33_get_tsr_data(); + // Check if int33 ISR actually points to something before calling it. + if (_dos_getvect(0x33)) { + // Call our magic function, which also returns our data segment + return int33_get_tsr_data(); + } else { + return 0; + } } else { // Get the TSR data of this instance, not the one currently installed // This is as simple as getting the data from this segment @@ -34,8 +34,12 @@ #define USE_VMWARE 1 /** Enable Windows 386/protected mode integration .*/ #define USE_WIN386 1 -/** Enable the wheel. */ +/** Enable the wheel (and ImPS/2 protocol). */ #define USE_WHEEL 1 +/** Enable Intellimouse Explorer protocol (two wheels and 5 buttons). */ +#define USE_IMEX 1 +/** Enable support for serial mouse. */ +#define USE_SERIAL 1 /** Trace mouse events verbosily. */ #define TRACE_EVENTS 0 /** Trace (noisy) API calls. */ @@ -51,14 +55,26 @@ #define USE_INTEGRATION (USE_VIRTUALBOX || USE_VMWARE) -/** Max size of PS/2 packet that we support. */ -#define MAX_PS2_PACKET_SIZE 4 +/** Max size of PS/2/serial packet that we support. */ +#define MAX_PACKET_SIZE 4 -/** Maximum number of 55ms ticks that may pass between two bytes of the same PS/2 packet */ -#define MAX_PS2_PACKET_DELAY 2 +/** Maximum number of 55ms ticks that may pass between two bytes of the same packet */ +#define MAX_PACKET_DELAY 2 -/** Number of buttons reported back to user programs. */ -#define NUM_BUTTONS 3 +/** Maximum COMn port. */ +#define MAX_PORTS 4 + +/** Maximum number of buttons supported by this driver. */ +#define MAX_BUTTONS 5 + +/** Maximum number of wheels supported. */ +#if USE_IMEX +#define MAX_WHEELS 2 +#elif USE_WHEEL +#define MAX_WHEELS 1 +#else +#define MAX_WHEELS 0 +#endif /** Size of int33 graphic cursor shape definitions. */ #define GRAPHIC_CURSOR_WIDTH 16 @@ -79,19 +95,28 @@ struct point { int16_t x, y; }; +enum { + WHEEL_DIR_UP, + WHEEL_DIR_DOWN +}; + typedef struct tsrdata { // TSR installation data + // Note that because of WatcomC limitations the offsets of these + // are hardcoded in the inline ASM, so do not change them. /** Previous int33 ISR, storing it for uninstall. */ void (__interrupt __far *prev_int33_handler)(); -#if USE_WIN386 + /** Previous ISR for IRQ 3 or 4, depending on the used serial port. */ + void (__interrupt __far *prev_irq3_4_handler)(); + /** Previous int2f ISR, may need to chain to it. */ void (__interrupt __far *prev_int2f_handler)(); -#endif + // Settings configured via the command line #if USE_WHEEL /** Whether to enable & use wheel mouse. */ bool usewheel; - /** Key (scancode) to generate on wheel scroll up/down, or 0 for none. */ - uint16_t wheel_up_key, wheel_down_key; + /** Scancode to generate on wheel N scroll up [N][0] or down [N][1]. 0 for none. */ + uint8_t wheel_key[MAX_WHEELS][2]; #endif // Video settings @@ -110,20 +135,28 @@ typedef struct tsrdata { struct point screen_granularity; // Detected mouse hardware & status -#if USE_WHEEL - /** Whether the current mouse has a wheel (and support is enabled). */ - bool haswheel; -#endif - /** Packet size that the BIOS is currently using. Either 1 (streaming) or 3 (plain). */ + /** Current mouse's port. 0 = PS/2 , 1...N serial. */ + uint8_t port; + /** Current negotiated PS/2 device_id (or serial mouse type) */ + uint8_t device_id; + /** Number of buttons of current device. */ + uint8_t num_buttons; + /** Number of wheels of current device. */ + uint8_t num_wheels; + /** Packet size that the PS/2 BIOS is currently using. Either 1 (streaming) or 3 (plain). */ uint8_t bios_packet_size; /** Packet size that we are currently expecting internally. Usually 3 (plain) or 4 (with wheel). */ uint8_t packet_size; /** For streaming mode: number of bytes received so far (< packet_size). */ uint8_t cur_packet_bytes; - /** Stores the bytes received so far (cur_bytes). */ - uint8_t ps2_packet[MAX_PS2_PACKET_SIZE]; + /** Stores the bytes received so far (cur_packet_bytes). */ + uint8_t cur_packet[MAX_PACKET_SIZE]; /** Number of ticks at the point when we started to receive this packet. */ uint16_t cur_packet_ticks; + /** IO base for the current port. */ + uint16_t port_io; + /** IRQ for the current port. */ + uint8_t port_irq; // Current mouse settings /** Mouse sensitivity/speed. */ @@ -168,18 +201,23 @@ typedef struct tsrdata { /** Ticks when the above value was last reset. */ uint16_t last_motion_ticks; /** Current status of buttons (as bitfield). */ - uint16_t buttons; + uint8_t buttons; struct { struct buttoncounter { + /** Last position of cursor where this button was pressed. */ struct point last; + /** Number of button presses since last button report. */ uint16_t count; } pressed, released; - } button[NUM_BUTTONS]; - /** Total delta movement of the wheel since the last wheel report. */ - int16_t wheel_delta; - /** Last position where the wheel was moved. */ - struct point wheel_last; - + } button[MAX_BUTTONS]; +#if USE_WHEEL + struct wheelcounter { + /** Total delta movement of the wheel since the last wheel report. */ + int16_t delta; + /** Last position where the wheel was moved. */ + struct point last; + } wheel[MAX_WHEELS]; +#endif // Cursor information /** Whether the cursor is currently displayed or not. */ bool cursor_visible; @@ -230,6 +268,8 @@ typedef TSRDATA __far * LPTSRDATA; extern void __declspec(naked) __far int33_isr(void); +extern void __declspec(naked) __far irq3_4_isr(void); + extern void __declspec(naked) __far int2f_isr(void); extern LPTSRDATA __far get_tsr_data(bool installed); @@ -18,7 +18,7 @@ */ #include <string.h> -#include <limits.h> +#include <stdlib.h> #include <windows.h> #include "utils.h" @@ -36,7 +36,7 @@ #define TRACE_EVENTS 0 /** Verbosely trace scroll wheel code. */ #define TRACE_WHEEL 0 -/** Number of lines to scroll per wheel event. */ +/** Number of lines to scroll per (vertical) wheel event. */ #define WHEEL_SCROLL_LINES 2 /** Windows 3.x only supports 1-2 mouse buttons anyway. */ @@ -98,9 +98,9 @@ static void send_event(unsigned short Status, short deltaX, short deltaY, short #if USE_WHEEL typedef struct { - /** Input: whether to find vertical scrollbars. */ - BOOL vertical; - /** Output: found scrollbar handle, or 0. */ + /** Input param: select scrollbars of either SBS_VERT or SBS_HORZ. */ + WORD style; + /** Output param: found scrollbar handle, or 0 if none found. */ HWND scrollbar; } FINDSCROLLBARDATA, FAR * LPFINDSCROLLBARDATA; @@ -118,7 +118,7 @@ static void print_window_name(HWND hWnd) #endif /** Helper function to traverse a window hierarchy and find a candidate scrollbar. */ -static BOOL CALLBACK __loadds find_scrollbar(HWND hWnd, LPARAM lParam) +static BOOL CALLBACK __loadds find_scrollbar_enum_proc(HWND hWnd, LPARAM lParam) { LPFINDSCROLLBARDATA data = (LPFINDSCROLLBARDATA) lParam; char buffer[16]; @@ -138,10 +138,11 @@ static BOOL CALLBACK __loadds find_scrollbar(HWND hWnd, LPARAM lParam) if (_fstrcmp(buffer, "ScrollBar") == 0) { LONG style = userapi.GetWindowLong(hWnd, GWL_STYLE); - if (data->vertical && (style & SBS_VERT)) { - data->scrollbar = hWnd; - return ENUM_CHILD_WINDOW_STOP; - } else if (!data->vertical && !(style & SBS_VERT)) { +#if TRACE_WHEEL + dprintf(DPREFIX "hWnd=0x%x is ScrollBar style=0x%lx\n", hWnd, style); +#endif + + if (data->style == (style & (SBS_HORZ|SBS_VERT))) { data->scrollbar = hWnd; return ENUM_CHILD_WINDOW_STOP; } @@ -150,21 +151,45 @@ static BOOL CALLBACK __loadds find_scrollbar(HWND hWnd, LPARAM lParam) return ENUM_CHILD_WINDOW_CONTINUE; } +/** Finds a scrollbar in the given window with the given style. + * @param style either SBS_HORZ or SBS_VERT. */ +static HWND find_scrollbar(HWND hWnd, WORD style) +{ + FINDSCROLLBARDATA data; + data.style = style; + data.scrollbar = 0; + +#if TRACE_WHEEL + dprintf(DPREFIX "find scrollbar on hWnd=0x%x with style=0x%x...\n", hWnd, style); +#endif + + userapi.EnumChildWindows(hWnd, find_scrollbar_enum_proc, (LONG) (LPVOID) &data); + +#if TRACE_WHEEL + dprintf(DPREFIX " found scrollbar 0x%x\n", data.scrollbar); +#endif + + return data.scrollbar; +} + /** Send scrolling messages to given window. * @param hWnd window to scroll. - * @param vertical true if vertical, false if horizontal. + * @param msg either WM_HSCROLL or WM_VSCROLL + * @param horizontal true if horizontal, false if vertical movement. * @param z number of lines to scroll. * @param hScrollBar corresponding scrollbar handle. */ -static void post_scroll_msg(HWND hWnd, BOOL vertical, int z, HWND hScrollBar) +static void post_scroll_msg(HWND hWnd, UINT msg, int z, HWND hScrollBar) { - UINT msg = vertical ? WM_VSCROLL : WM_HSCROLL; - WPARAM wParam = z < 0 ? SB_LINEUP : SB_LINEDOWN; + WPARAM wParam = z < 0 ? SB_LINEUP : SB_LINEDOWN; // Same value as z < 0 ? SB_LINELEFT : SB_LINERIGHT LPARAM lParam = MAKELPARAM(0, hScrollBar); - UINT i, lines = (z < 0 ? -z : z) * WHEEL_SCROLL_LINES; + UINT i, lines = abs(z); + + // Only vertical scroll gets multipled by speed factor + if (msg == WM_VSCROLL) lines *= WHEEL_SCROLL_LINES; #if TRACE_WHEEL - dprintf("w16mouse: sending scroll msg to hWnd=0x%x from=0x%x vert=%d lines=%u\n", hWnd, hScrollBar, vertical, lines); + dprintf(DPREFIX "sending scroll msg 0x%x to hWnd=0x%x from=0x%x dir=%u lines=%u\n", msg, hWnd, hScrollBar, wParam, lines); #endif for (i = 0; i < lines; i++) { @@ -174,13 +199,13 @@ static void post_scroll_msg(HWND hWnd, BOOL vertical, int z, HWND hScrollBar) } /** Send wheel scrolling events to the most likely candidate window. */ -static void send_wheel_movement(int8_t z) +static void send_wheel_movement(int8_t z, BOOL horizontal) { POINT point; HWND hWnd; #if TRACE_WHEEL - dprintf("w16mouse: wheel=%d\n", z); + dprintf(DPREFIX "wheel=%d %s\n", z, horizontal ? "horiz" : "vert"); #endif // TODO It's highly unlikely that we can call this many functions from @@ -192,7 +217,7 @@ static void send_wheel_movement(int8_t z) hWnd = userapi.WindowFromPoint(point); #if TRACE_WHEEL - dprintf("w16mouse: initial hWnd=0x%x\n", hWnd); + dprintf(DPREFIX "initial hWnd=0x%x\n", hWnd); #endif while (hWnd) { @@ -203,54 +228,55 @@ static void send_wheel_movement(int8_t z) dprintf(DPREFIX "hWnd=0x%x style=0x%lx\n", hWnd, style); #endif - if (style & WS_VSCROLL) { -#if TRACE_WHEEL - dprintf(DPREFIX "found WS_VSCROLL\n"); -#endif - post_scroll_msg(hWnd, TRUE, z, 0); - break; - } else if (style & WS_HSCROLL) { -#if TRACE_WHEEL - dprintf(DPREFIX "found WS_HSCROLL\n"); -#endif - post_scroll_msg(hWnd, FALSE, z, 0); - break; - } else { - FINDSCROLLBARDATA data; - - // Let's check if we can find a vertical scroll bar in this window.. -#if TRACE_WHEEL - dprintf(DPREFIX "find vertical scrollbar...\n"); -#endif - data.vertical = TRUE; - data.scrollbar = 0; - userapi.EnumChildWindows(hWnd, find_scrollbar, (LONG) (LPVOID) &data); - if (data.scrollbar) { - post_scroll_msg(hWnd, TRUE, z, data.scrollbar); + if (!horizontal) { + // Check if this window is vertically scrollable... + if (style & WS_VSCROLL) { + post_scroll_msg(hWnd, WM_VSCROLL, z, 0); break; - } - - // Try a horizontal scrollbar now -#if TRACE_WHEEL - dprintf(DPREFIX "find horizontal scrollbar...\n"); -#endif - data.vertical = FALSE; - data.scrollbar = 0; - userapi.EnumChildWindows(hWnd, find_scrollbar, (LONG) (LPVOID) &data); - if (data.scrollbar) { - post_scroll_msg(hWnd, FALSE, z, data.scrollbar); + } else if (style & WS_HSCROLL) { + post_scroll_msg(hWnd, WM_HSCROLL, z, 0); break; - } - - // Otherwise, try again on the parent window - if (style & WS_CHILD) { -#if TRACE_WHEEL - dprintf(DPREFIX "go into parent...\n"); -#endif - hWnd = userapi.GetParent(hWnd); } else { - // This was already a topmost window + // Otherwise, let's see if we can find a vertical scroll bar in this window.. + HWND scrollbar = find_scrollbar(hWnd, SBS_VERT); + if (scrollbar) { + post_scroll_msg(hWnd, WM_VSCROLL, z, scrollbar); + break; + } + + // If no vertical scrollbar... try a horizontal scrollbar second + scrollbar = find_scrollbar(hWnd, SBS_HORZ); + if (scrollbar) { + post_scroll_msg(hWnd, WM_HSCROLL, z, scrollbar); + break; + } + + // Otherwise, continue search on the parent window + if (style & WS_CHILD) { + hWnd = userapi.GetParent(hWnd); + } else { + // This was already a topmost window + break; + } + } + } else { + // Similar to above except we try only horizontal scrollbars + if (style & WS_HSCROLL) { + post_scroll_msg(hWnd, WM_HSCROLL, z, 0); break; + } else { + HWND scrollbar = find_scrollbar(hWnd, SBS_HORZ); + if (scrollbar) { + post_scroll_msg(hWnd, WM_HSCROLL, z, scrollbar); + break; + } + + if (style & WS_CHILD) { + hWnd = userapi.GetParent(hWnd); + } else { + // This was already a topmost window + break; + } } } } @@ -262,7 +288,7 @@ static void send_wheel_movement(int8_t z) #endif /* USE_WHEEL */ /** Called by the int33 mouse driver. */ -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) +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; @@ -282,11 +308,12 @@ static void FAR int33_mouse_callback(uint16_t events, uint16_t buttons, int16_t } #if USE_WHEEL - if (flags.wheel && (events & INT33_EVENT_MASK_WHEEL_MOVEMENT)) { + if (flags.wheel && (events & INT33_EVENT_MASK_ANY_WHEEL_MOVEMENT)) { // If wheel API is enabled, higher byte of buttons contains wheel movement int8_t z = (buttons & 0xFF00) >> 8; + BOOL horizontal = !!(events & INT33_EVENT_MASK_HORIZ_WHEEL_MOVEMENT); if (z) { - send_wheel_movement(z); + send_wheel_movement(z, horizontal); } } #endif @@ -455,7 +482,7 @@ BOOL FAR PASCAL LibMain(HINSTANCE hInstance, WORD wDataSegment, /** Called by Windows to retrieve information about the mouse hardware. */ WORD FAR PASCAL Inquire(LPMOUSEINFO lpMouseInfo) { - lpMouseInfo->msExist = 1; + lpMouseInfo->msExist = TRUE; lpMouseInfo->msRelative = 0; lpMouseInfo->msNumButtons = MOUSE_NUM_BUTTONS; lpMouseInfo->msRate = 80; @@ -544,8 +571,10 @@ VOID FAR PASCAL Disable(VOID) } } -/** Called by Window to retrieve the interrupt vector number used by this driver, or -1. */ +/** Called by Windows to retrieve the interrupt vector number used by this driver, or -1. */ int FAR PASCAL MouseGetIntVect(VOID) { + // Unclear if this is actually needed, or whether it needs the hw interrupt + // rather than the software one. return 0x33; } diff --git a/mousew16.lnk b/mousew16.lnk index b8a068c..ad6ac85 100644 --- a/mousew16.lnk +++ b/mousew16.lnk @@ -1,9 +1,13 @@ system windows_dll + option map=mousew16.map option modname=MOUSE # This is necessary; USER.EXE imports mouse functions using this module name option description 'VBMouse int33 absolute mouse driver' -segment CALLBACKS fixed shared # We need a non-moveable segment to store our callback routines +option heapsize=128 # We don't need localheap but windows_dll sets 1K by default + +segment CALLBACKS fixed shared # We need a non-moveable segment to store our callback routines +segment CLASS DATA fixed shared # Likewise fix the data segment, too cumbersome if not export Inquire.1 export Enable.2 @@ -28,20 +28,31 @@ #include "int33.h" #include "int21dos.h" #include "int15ps2.h" +#include "serial.h" +#include "sermouse.h" #include "vbox.h" #include "vmware.h" #include "dostsr.h" #include "mousetsr.h" -static nl_catd cat; - #if USE_WHEEL -static void detect_wheel(LPTSRDATA data) +static bool detect_ps2_wheel(LPTSRDATA data) { // 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(_(1, 0, "Wheel mouse found and enabled\n")); + if (ps2m_detect_imps2()) { + if (ps2m_detect_imex()) { + data->num_wheels = 2; + data->num_buttons = 5; + } else { + data->num_wheels = 1; + data->num_buttons = 3; + } + return true; + } else { + data->num_wheels = 0; + data->num_buttons = 3; + return false; } } @@ -51,31 +62,40 @@ static int set_wheel(LPTSRDATA data, bool enable) data->usewheel = enable; if (data->usewheel) { - detect_wheel(data); - if (!data->haswheel) { + if (data->port == 0) { + if (!detect_ps2_wheel(data)) { + fprintf(stderr, _(3, 0, "Could not find PS/2 wheel mouse\n")); + } + } else { + // TODO Serial does not support wheels yet fprintf(stderr, _(3, 0, "Could not find PS/2 wheel mouse\n")); } } else { - data->haswheel = false; + // Force num_wheels to 0 even before the next mouse reset + data->num_wheels = 0; } return 0; } +static void set_wheel_keys(LPTSRDATA data, int wheel, uint8_t scancode_up, uint8_t scancode_down) +{ + data->wheel_key[wheel][WHEEL_DIR_UP] = scancode_up; + data->wheel_key[wheel][WHEEL_DIR_DOWN] = scancode_down; +} + static int set_wheel_key(LPTSRDATA data, const char *keyname) { - if (!data->usewheel || !data->haswheel) { + if (!data->usewheel) { fprintf(stderr, _(3, 1, "Wheel not detected or support not enabled\n")); return EXIT_FAILURE; } if (keyname) { if (stricmp(keyname, "updn") == 0) { - data->wheel_up_key = 0x4800; - data->wheel_down_key = 0x5000; + set_wheel_keys(data, 0, 0x48, 0x50); printf(_(1, 4, "Generate Up Arrow / Down Arrow key presses on wheel movement\n")); } else if (stricmp(keyname, "pageupdn") == 0) { - data->wheel_up_key = 0x4900; - data->wheel_down_key = 0x5100; + set_wheel_keys(data, 0, 0x49, 0x51); printf(_(1, 5, "Generate PageUp / PageDown key presses on wheel movement\n")); } else { fprintf(stderr, _(3, 2, "Unknown key '%s'\n"), keyname); @@ -83,15 +103,35 @@ static int set_wheel_key(LPTSRDATA data, const char *keyname) } } else { printf(_(1, 6, "Disabling wheel keystroke generation\n")); - data->wheel_up_key = 0; - data->wheel_down_key = 0; + set_wheel_keys(data, 0, 0, 0); + } + return EXIT_SUCCESS; +} + +static int set_hwheel_key(LPTSRDATA data, const char *keyname) +{ + if (!data->usewheel) { + fprintf(stderr, _(3, 1, "Wheel not detected or support not enabled\n")); + return EXIT_FAILURE; + } + if (keyname) { + if (stricmp(keyname, "lr") == 0) { + set_wheel_keys(data, 1, 0x4B, 0x4D); + printf(_(1, 4, "Generate Up Arrow / Down Arrow key presses on wheel movement\n")); + } else { + fprintf(stderr, _(3, 2, "Unknown key '%s'\n"), keyname); + return EXIT_FAILURE; + } + } else { + printf(_(1, 6, "Disabling wheel keystroke generation\n")); + set_wheel_keys(data, 1, 0, 0); } return EXIT_SUCCESS; } #endif /* USE_WHEEL */ #if USE_VIRTUALBOX -static int set_virtualbox_integration(LPTSRDATA data, bool enable) +static int set_virtualbox_integration(LPTSRDATA data, bool enable, bool verbose) { if (enable) { int err; @@ -100,19 +140,19 @@ static int set_virtualbox_integration(LPTSRDATA data, bool enable) err = vbox_init_device(&data->vb); if (err) { - fprintf(stderr, _(3, 3, "Cannot find VirtualBox PCI device, err=%d\n"), err); + if (verbose) fprintf(stderr, _(3, 3, "Cannot find VirtualBox PCI device, err=%d\n"), err); return err; } err = vbox_init_buffer(&data->vb, VBOX_BUFFER_SIZE); if (err) { - fprintf(stderr, _(3, 4, "Cannot lock buffer used for VirtualBox communication, err=%d\n"), err); + if (verbose) fprintf(stderr, _(3, 4, "Cannot lock buffer used for VirtualBox communication, err=%d\n"), err); return err; } err = vbox_report_guest_info(&data->vb, VBOXOSTYPE_DOS); if (err) { - fprintf(stderr, _(3, 5, "VirtualBox communication is not working, err=%d\n"), err); + if (verbose) fprintf(stderr, _(3, 5, "VirtualBox communication is not working, err=%d\n"), err); return err; } @@ -129,7 +169,7 @@ static int set_virtualbox_integration(LPTSRDATA data, bool enable) data->vbavail = false; data->vbhaveabs = false; } else { - printf(_(1, 9, "VirtualBox integration already disabled or not available\n")); + if (verbose) printf(_(1, 9, "VirtualBox integration already disabled or not available\n")); } } @@ -146,7 +186,7 @@ static int set_virtualbox_host_cursor(LPTSRDATA data, bool enable) #endif #if USE_VMWARE -static int set_vmware_integration(LPTSRDATA data, bool enable) +static int set_vmware_integration(LPTSRDATA data, bool enable, bool verbose) { if (enable) { int32_t version; @@ -157,11 +197,11 @@ static int set_vmware_integration(LPTSRDATA data, bool enable) version = vmware_get_version(); if (version < 0) { - fprintf(stderr, _(3, 6, "Could not detect VMware, err=%ld\n"), version); + if (verbose) fprintf(stderr, _(3, 6, "Could not detect VMware, err=%ld\n"), version); return -1; } - printf(_(1, 11, "Found VMware protocol version %ld\n"), version); + if (verbose) printf(_(1, 11, "Found VMware protocol version %ld\n"), version); vmware_abspointer_cmd(VMWARE_ABSPOINTER_CMD_ENABLE); @@ -187,7 +227,7 @@ static int set_vmware_integration(LPTSRDATA data, bool enable) data->vmwavail = false; printf(_(1, 13, "Disabled VMware integration\n")); } else { - printf(_(1, 14, "VMware integration already disabled or not available\n")); + if (verbose) printf(_(1, 14, "VMware integration already disabled or not available\n")); } } @@ -195,7 +235,7 @@ static int set_vmware_integration(LPTSRDATA data, bool enable) } #endif -static int set_integration(LPTSRDATA data, bool enable) +static int set_integration(LPTSRDATA data, bool enable, bool verbose) { if (enable) { int err = -1; @@ -203,13 +243,13 @@ static int set_integration(LPTSRDATA data, bool enable) #if USE_VIRTUALBOX // First check if we can enable the VirtualBox integration, // since it's a PCI device it's easier to check if it's not present - err = set_virtualbox_integration(data, true); + err = set_virtualbox_integration(data, true, verbose); if (!err) return 0; #endif #if USE_VMWARE // Afterwards try VMWare integration - err = set_vmware_integration(data, true); + err = set_vmware_integration(data, true, verbose); if (!err) return 0; #endif @@ -218,12 +258,12 @@ static int set_integration(LPTSRDATA data, bool enable) } else { #if USE_VIRTUALBOX if (data->vbavail) { - set_virtualbox_integration(data, false); + set_virtualbox_integration(data, false, verbose); } #endif #if USE_VMWARE if (data->vmwavail) { - set_vmware_integration(data, false); + set_vmware_integration(data, false, verbose); } #endif return 0; @@ -241,31 +281,33 @@ static int set_host_cursor(LPTSRDATA data, bool enable) return -1; } -static int configure_driver(LPTSRDATA data) +static int configure_driver_ps2(LPTSRDATA data) { int err; - // Configure the debug logging port - dlog_init(); - // Check for PS/2 mouse BIOS availability - if ((err = ps2m_init(PS2M_PACKET_SIZE_PLAIN))) { - fprintf(stderr, _(3, 8, "Cannot init PS/2 mouse BIOS, err=%d\n"), err); - // Can't do anything without PS/2 + if ((err = ps2m_init(PS2M_PACKET_SIZE_STD))) { return err; } + // OK, if BIOS gives no error, assume PS/2 mouse is present and use it + data->port = 0; + data->port_io = 0; + data->port_irq = 0; + #if USE_WHEEL // Let's utilize the wheel by default data->usewheel = true; - data->wheel_up_key = 0; - data->wheel_down_key = 0; - detect_wheel(data); + detect_ps2_wheel(data); +#else + // Without ImEX/wheel detection, hardcode number of buttons + data->num_buttons = 3; + data->num_wheels = 0; #endif #if USE_INTEGRATION // Enable integration by default - set_integration(data, true); + set_integration(data, true, false); #endif #if USE_VIRTUALBOX @@ -276,6 +318,196 @@ static int configure_driver(LPTSRDATA data) return 0; } +#if USE_SERIAL +static int configure_driver_serial(LPTSRDATA data, int port, unsigned iobase) +{ + struct serial_config serconfig; + unsigned delay; + char sig = 0; + + if (!iobase) { + dprintf("Skip search on COM%u because no IO base found\n", port); + return -1; + } + +#if ENABLE_DLOG && DLOG_TARGET == DLOG_TARGET_SERIAL + if (iobase == DLOG_TARGET_PORT) { + dprintf("Skip search on COM%u because it would overlap with dlog %x\n", port, iobase); + return -2; + } +#endif + + dprintf("Detecting mouse on COM%d, iobase %xh\n", port, iobase); + serial_save_config(iobase, &serconfig); + serial_configure_interrupts(iobase, SERIAL_IER_NONE); // disable interrupts for now + // 1200 bps, word length = 7, parity = none + serial_configure_line(iobase, SERIAL_DIVISOR_1200, SERIAL_LCR_WL_7|SERIAL_LCR_PAR_NONE); + serial_set_fifo_control(iobase, SERIAL_FCR_FIFO_RESET|SERIAL_FCR_FIFO_ENABLE); + // Enables DRT, but clears RTS, so that mouse enters reset state + serial_set_modem_control(iobase, SERIAL_MCR_DTR); + dputs("DTR ON, RTS OFF"); + + // Wait for 3 ticks (100-150ms) for mouse to power off + bda_wait_ticks(3); + + // Now turn on the mouse by enabling RTS + serial_set_modem_control(iobase, SERIAL_MCR_DTR | SERIAL_MCR_RTS); + dputs("DTR ON, RTS ON"); + + // Wait up to 10 ticks (550ms) for the mouse to send something we understand + for (delay = 10; delay > 0; delay--) { + while (serial_data_ready(iobase)) { + uint8_t car = serial_read_data(iobase); + + // Is this something we recognize ? + dprintf("serial data after reset: 0x%hx '%c'\n", car, car); + if (car == 'M') { + // Ok, got the MS signature, break out of this wait + sig = car; + break; + } + } + + if (sig) break; + bda_wait_tick(); + dprintf("remaining ticks %u\n", delay); + } + + + switch (sig) { + case 'M': + // We have a valid MS-style serial mouse + data->port = port; + data->device_id = SERMOUSE_DEVICE_ID_MS; + data->port_io = iobase; + data->port_irq = serial_get_irq(port); + data->num_buttons = 2; + data->num_wheels = 0; + data->packet_size = 3; + + // Wait for an extended signature for about 2 ticks (50-100ms) + sig = 0; + for (delay = 2; delay > 0; delay--) { + while (serial_data_ready(iobase)) { + uint8_t car = serial_read_data(iobase); + + dprintf("serial data after 'M': 0x%hx '%c'\n", car, car); + if (car == '3' || car == 'Z') { + sig = car; + break; + } + } + + if (sig) break; + bda_wait_tick(); + dprintf("2nd loop remaining ticks %u\n", delay); + } + + // Update the mouse details based on the extended signature + // We don't change the device ID since we treat all the same. + switch (sig) { + case '3': + data->packet_size = 4; + data->num_buttons = 3; + break; + case 'Z': + data->packet_size = 4; + data->num_buttons = 3; +#if USE_WHEEL + data->num_wheels = 1; +#endif + break; + } + + // In any case, we got ourselves a mouse. + return 0; + } + + // No mouse found on this port. + serial_restore_config(iobase, &serconfig); + return -3; +} +#endif + +/// Auto-configure the TSR. Try to detect which mouse we have connected and enable all possible integrations. +static int configure_driver(LPTSRDATA data, unsigned num_ports, int ports[]) +{ + int errs[MAX_PORTS+1] = {0}; + unsigned portidx; + bool found_mouse = false; + + // Configure the debug logging port + dlog_init(); + + // Search for a mouse + for (portidx = 0; portidx < num_ports; portidx++) { + int port = ports[portidx]; + if (port == 0) { + // PS/2 + int err = configure_driver_ps2(data); + if (!err) { + printf(_(1, 0, "Found PS/2 mouse with %u buttons, %u wheels\n"), + data->num_buttons, data->num_wheels); + found_mouse = true; + break; + } else { + // Remember error to print out later only if no mouse was found + errs[portidx] = err; + } + } else { +#if USE_SERIAL + int err = configure_driver_serial(data, port, serial_get_iobase(port)); + if (!err) { + printf(_(1, 22, "Found serial mouse on COM%u with %u buttons, %u wheels\n"), + port, data->num_buttons, data->num_wheels); + found_mouse = true; + break; + } else { + errs[portidx] = err; + } +#endif + } + } + + if (!found_mouse) { + // If we are here, we have neither serial nor PS/2. + // Print all error messages now. + for (portidx = 0; portidx < num_ports; portidx++) { + int port = ports[portidx]; + int err = errs[portidx]; + + if (err) { + if (port == 0) { + fprintf(stderr, _(3, 8, "Cannot init PS/2 mouse BIOS, err=%d\n"), err); + } else { + fprintf(stderr, _(3, 15, "Cannot find mouse in COM%u, err=%d\n"), port, err); + } + } + } + + fprintf(stderr, _(3, 14, "No mouse found\n")); + return -1; + } + + // Otherwise, we found a mouse, go ahead with installation + + // No more interruptions from now on and until we TSR. + // Inserting ourselves in the interrupt chain should be atomic. + _disable(); + + // Hook hardware interrupts. Which interrupts depends on which mouse was found. + data->prev_irq3_4_handler = 0; +#if USE_SERIAL + if (data->port_irq) { + dprintf("Hooking irq%hu (int%x) for serial\n", data->port_irq, 0x8 + data->port_irq); + data->prev_irq3_4_handler = _dos_getvect(0x8 + data->port_irq); + _dos_setvect(0x8 + data->port_irq, data:>irq3_4_isr); + } +#endif + + return 0; +} + static int move_driver_to_umb(LPTSRDATA __far * data) { segment_t cur_seg = FP_SEG(*data); @@ -294,16 +526,17 @@ static __declspec(aborts) int install_driver(LPTSRDATA data, bool high) { const unsigned int resident_size = DOS_PSP_SIZE + get_resident_size(); - // No more interruptions from now on and until we TSR. - // Inserting ourselves in the interrupt chain should be atomic. _disable(); + // Hook main interrupts data->prev_int33_handler = _dos_getvect(0x33); _dos_setvect(0x33, data:>int33_isr); #if USE_WIN386 data->prev_int2f_handler = _dos_getvect(0x2f); _dos_setvect(0x2f, data:>int2f_isr); +#else + data->prev_int2f_handler = 0; #endif printf(_(1, 17, "Driver installed\n")); @@ -323,62 +556,101 @@ static __declspec(aborts) int install_driver(LPTSRDATA data, bool high) static bool check_if_driver_uninstallable(LPTSRDATA data) { - void (__interrupt __far *cur_int33_handler)() = _dos_getvect(0x33); - // Compare the segment of the installed handler to see if its ours // or someone else's - if (FP_SEG(cur_int33_handler) != FP_SEG(data)) { - fprintf(stderr, _(3, 9, "INT33 has been hooked by someone else, cannot safely remove\n")); - return false; + if (data->prev_int33_handler) { + void (__interrupt __far *cur_int33_handler)() = _dos_getvect(0x33); + + if (FP_SEG(cur_int33_handler) != FP_SEG(data)) { + fprintf(stderr, _(3, 9, "INT%X has been hooked by someone else, cannot safely remove\n"), 0x33); + return false; + } } -#if USE_WIN386 - { + if (data->port_irq && data->prev_irq3_4_handler) { + void (__interrupt __far *cur_irq3_4_handler)() = _dos_getvect(0x8 + data->port_irq); + + if (FP_SEG(cur_irq3_4_handler) != FP_SEG(data)) { + fprintf(stderr, _(3, 9, "INT%X has been hooked by someone else, cannot safely remove\n"), 0x8 + data->port_irq); + return false; + } + } + + if (data->prev_int2f_handler) { void (__interrupt __far *cur_int2f_handler)() = _dos_getvect(0x2f); if (FP_SEG(cur_int2f_handler) != FP_SEG(data)) { - fprintf(stderr, _(3, 10, "INT2F has been hooked by someone else, cannot safely remove\n")); + fprintf(stderr, _(3, 9, "INT%X has been hooked by someone else, cannot safely remove\n"), 0x2F); return false; } } -#endif return true; } -static int unconfigure_driver(LPTSRDATA data) +static void unconfigure_driver(LPTSRDATA data) { + if (data->port == 0) { + // PS/2 mode + + // Turn off PS2 BIOS, and remove callback + ps2m_enable(false); + ps2m_set_callback(0); + #if USE_INTEGRATION - set_integration(data, false); + set_integration(data, false, false); #endif + } else { +#if USE_SERIAL + // Serial mode + uint16_t iobase = data->port_io; - ps2m_enable(false); - ps2m_set_callback(0); + // Disable interrupts, and clear RTS/DTS, which should completely power down mouse + serial_configure_interrupts(iobase, 0); + serial_set_modem_control(iobase, 0); - return 0; + // TODO Should I mask the serial IRQ on the PIC too? +#endif + } + + // Unhook only configured hardware-related interrupts + _disable(); + if (data->port_irq && data->prev_irq3_4_handler) { + _dos_setvect(0x8 + data->port_irq, data->prev_irq3_4_handler); + data->prev_irq3_4_handler = 0; + } + _enable(); } -static int uninstall_driver(LPTSRDATA data) +static void uninstall_driver(LPTSRDATA data) { - _dos_setvect(0x33, data->prev_int33_handler); - -#if USE_WIN386 - _dos_setvect(0x2f, data->prev_int2f_handler); -#endif + // Unhook all interrupts + _disable(); + if (data->prev_int33_handler) { + _dos_setvect(0x33, data->prev_int33_handler); + data->prev_int33_handler = 0; + } + if (data->port_irq && data->prev_irq3_4_handler) { + _dos_setvect(0x8 + data->port_irq, data->prev_irq3_4_handler); + data->prev_irq3_4_handler = 0; + } + if (data->prev_int2f_handler) { + _dos_setvect(0x2f, data->prev_int2f_handler); + data->prev_int2f_handler = 0; + } + _enable(); // Find and deallocate the PSP (including the entire program), // it is always 256 bytes (16 paragraphs) before the TSR segment dos_free(FP_SEG(data) - (DOS_PSP_SIZE/16)); printf(_(1, 18, "Driver uninstalled\n")); - - return 0; } static int driver_reset(void) { printf(_(1, 19, "Reset mouse driver\n")); - return int33_reset() == 0xFFFF; + return int33_reset() == INT33_MOUSE_FOUND; } static int driver_not_found(void) @@ -396,17 +668,27 @@ static void print_help(void) puts(_(0, 2, "Supported actions and options:")); puts(_(0, 3, " install Install the driver (default).")); puts(_(0, 4, " low Install in conventional memory (otherwise UMB).")); - puts(_(0, 5, " uninstall Uninstall the driver from memory.")); +#if USE_SERIAL + puts(_(0, 5, " ps2, com1, com2, ... Specify on which ports to search a mouse.")); +#endif + puts(_(0, 6, " uninstall Uninstall the driver from memory.")); +#if USE_SERIAL + puts(_(0, 7, " rescan <ps2|com1|..> Re-scan for mouse in specified ports.")); +#endif #if USE_WHEEL - puts(_(0, 6, " wheel <ON|OFF> Enable/disable wheel API support.")); - puts(_(0, 7, " wheelkey <KEY|OFF> Emulate a specific keystroke on wheel scroll.")); - puts(_(0, 8, " Supported keys: updn, pageupdn.")); + puts(_(0, 8, " wheel <ON|OFF> Enable/disable wheel API support.")); + puts(_(0, 9, " wheelkey <KEY|OFF> Emulate a specific keystroke on wheel scroll.")); + puts(_(0, 10, " Supported keys: updn, pageupdn.")); +#if USE_IMEX + puts(_(0, 11, " Supported keys: updn, pageupdn.")); + puts(_(0, 12, " Supported keys: updn, pageupdn.")); +#endif #endif #if USE_INTEGRATION - puts(_(0, 9, " integ <ON|OFF> Enable/disable VirtualBox integration.")); - puts(_(0, 10, " hostcur <ON|OFF> Enable/disable mouse cursor rendering in the host.")); + puts(_(0, 13, " integ <ON|OFF> Enable/disable VirtualBox integration.")); + puts(_(0, 14, " hostcur <ON|OFF> Enable/disable mouse cursor rendering in the host.")); #endif - puts(_(0, 11, " reset Reset mouse driver.")); + puts(_(0, 15, " reset Reset mouse driver.")); } static int invalid_arg(const char *s) @@ -445,28 +727,66 @@ static bool is_false(const char *s) || stricmp(s, "0") == 0; } +static int parse_port(const char *s) +{ + if (stricmp(s, "ps2") == 0) { + return 0; +#if USE_SERIAL + } else if (strnicmp(s, "com", 3) == 0 && s[4] == '\0' + && s[3] >= '0' && s[3] <= '9') { + // "COMn" string + return s[3] - '0'; +#endif + } else { + return -1; + } +} + +static int default_port_order(int ports[]) +{ + const unsigned num_com_ports = MIN(serial_num_ports(), MAX_PORTS); + unsigned portidx = 0, port; + if (ps2m_installed()) ports[portidx++] = 0; // 1st. PS2 + // 2nd. COMn in order + for (port = 1; port <= num_com_ports; port++) { + ports[portidx++] = port; + } + return portidx; +} + int main(int argc, const char *argv[]) { LPTSRDATA data = get_tsr_data(true); int err, argi = 1; - cat = kittenopen("vbmouse"); + kittenopen(argv[0]); printf(_(1, 20, "\nVBMouse %x.%x (reporting as MSMOUSE %x.%x)\n"), VERSION_MAJOR, VERSION_MINOR, REPORTED_VERSION_MAJOR, REPORTED_VERSION_MINOR); if (argi >= argc || stricmp(argv[argi], "install") == 0) { bool high = true; + int portidx = 0; + int ports[1+MAX_PORTS] = {-1}; argi++; for (; argi < argc; argi++) { + int port; if (stricmp(argv[argi], "low") == 0) { high = false; } else if (stricmp(argv[argi], "high") == 0) { high = true; + } else if ((port = parse_port(argv[argi])) >= 0) { + if (portidx > MAX_PORTS) return invalid_arg(argv[argi]); + if (port < 0 || port > MAX_PORTS) return invalid_arg(argv[argi]); + ports[portidx++] = port; } else { return invalid_arg(argv[argi]); } } + if (!portidx) { + // User has not specified custom search order, so init with default + portidx = default_port_order(ports); + } if (data) { printf(_(1, 21, "VBMouse already installed\n")); @@ -481,22 +801,57 @@ int main(int argc, const char *argv[]) } else { deallocate_environment(_psp); } - err = configure_driver(data); + err = configure_driver(data, portidx, ports); if (err) { if (high) cancel_reallocation(FP_SEG(data)); return EXIT_FAILURE; } + return install_driver(data, high); } 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); + unconfigure_driver(data); + uninstall_driver(data); + return EXIT_SUCCESS; + } else if (stricmp(argv[argi], "rescan") == 0) { + int portidx = 0, port; + int ports[1+MAX_PORTS] = {-1}; + + argi++; + for (; argi < argc; argi++) { + int port; + if ((port = parse_port(argv[argi])) >= 0) { + if (portidx > MAX_PORTS) return invalid_arg(argv[argi]); + if (port < 0 || port > MAX_PORTS) return invalid_arg(argv[argi]); + ports[portidx++] = port; + } else { + return invalid_arg(argv[argi]); + } + } + if (!portidx) { + portidx = default_port_order(ports); + } + + // Unhook the driver + if (!data) return driver_not_found(); + if (!check_if_driver_uninstallable(data)) { + return EXIT_FAILURE; + } + unconfigure_driver(data); + + // Configure the driver again + err = configure_driver(data, portidx, ports); if (err) { return EXIT_FAILURE; } - return uninstall_driver(data); + + // Force a reset of the driver + driver_reset(); + + return EXIT_SUCCESS; #if USE_WHEEL } else if (stricmp(argv[argi], "wheel") == 0) { bool enable = true; @@ -527,6 +882,24 @@ int main(int argc, const char *argv[]) } else { return set_wheel_key(data, 0); } + } else if (stricmp(argv[argi], "hwheelkey") == 0) { + bool enable = true; + const char *key = 0; + + if (!data) return driver_not_found(); + + argi++; + if (argi < argc) { + if (is_false(argv[argi])) enable = false; + else key = argv[argi]; + } + + if (enable) { + if (!key) return arg_required("hwheelkey"); + return set_hwheel_key(data, key); + } else { + return set_hwheel_key(data, 0); + } #endif #if USE_INTEGRATION } else if (stricmp(argv[argi], "integ") == 0) { @@ -539,7 +912,7 @@ int main(int argc, const char *argv[]) if (is_false(argv[argi])) enable = false; } - return set_integration(data, enable); + return set_integration(data, enable, true); } else if (stricmp(argv[argi], "hostcur") == 0) { bool enable = true; diff --git a/moustest.c b/moustest.c new file mode 100644 index 0000000..3270c9c --- /dev/null +++ b/moustest.c @@ -0,0 +1,814 @@ +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <ctype.h> + +#include <dos.h> +#include <conio.h> +#include <graph.h> + +#include "int33.h" +#include "int16kbd.h" +#include "utils.h" +#include "dlog.h" + +#define DPREFIX "moustest: " + +enum T_COLOR { + T_BLACK = 0, + T_BLUE = 1, + T_GREEN = 2, + T_CYAN = 3, + T_RED = 4, + T_MAGENTA = 5, + T_BROWN = 6, + T_WHITE = 7, + T_BRIGHT = 8 +}; + +bool main_exiting = false; + +/// Do not automatically reset the mouse driver +bool args_no_reset = false; +/// Do not query for wheel features +bool args_no_wheel = false; + +uint16_t driver_caps; +uint16_t num_buttons; +uint16_t num_wheels; + +struct mouseevent { + uint16_t events; + uint8_t buttons; + int16_t x; + int16_t y; + int8_t z; + int16_t delta_x; + int16_t delta_y; +} lastmouseevent; + +struct videoconfig vidconf; +bool vidconf_is_graphics, vidconf_is_color; + +struct textsettings txtconf; + +short ctexttop, ctextbot, ctextleft, ctextright; +struct rccoord ctextpos; +const short ctextcolor = T_WHITE; +const long ctextbg = _BLACK; + +typedef enum gui_palette { + GUI_COLOR_CONSOLE, + GUI_COLOR_BAR, + GUI_COLOR_WINDOW, + GUI_COLOR_LIST, + GUI_COLOR_LIST_SEL, + + GUI_COLOR_COUNT +} gui_color; +long gui_bg_color[GUI_COLOR_COUNT]; +int gui_text_color[GUI_COLOR_COUNT]; + +void gui_print(gui_color color, const char *str) +{ + if (vidconf_is_graphics) { + const int char_width = vidconf.numxpixels / vidconf.numtextcols; + const int char_height = vidconf.numypixels / vidconf.numtextrows; + const int text_chars = strlen(str); + const int text_width = text_chars * char_width; + const int text_height = char_height; // No multiline support. + struct rccoord textpos = _gettextposition(); + const int text_x = char_width * (textpos.col - 1); + const int text_y = char_height * (textpos.row - 1); + + _setcolor(gui_bg_color[color]); + _rectangle(_GFILLINTERIOR, text_x, text_y, text_x + text_width, text_y + text_height); + + _setcolor(gui_text_color[color]); + _moveto(text_x, text_y); + _outgtext((char*) str); + + textpos.col += text_chars; + _settextposition(textpos.row, textpos.col); + } else { + _settextcolor(gui_text_color[color]); + _setbkcolor(gui_bg_color[color]); + _outtext((char*) str); + } +} + +void gui_printf(gui_color color, const char *format, ...) +{ + va_list arglist; + char buffer[120]; + + va_start(arglist, format); + vsprintf(buffer, format, arglist); + va_end(arglist); + + gui_print(color, buffer); +} + +void gui_draw_textrectangle(short top, short left, short bottom, short right, gui_color color) +{ + const short char_width = vidconf.numxpixels / vidconf.numtextcols; + const short char_height = vidconf.numypixels / vidconf.numtextrows; + short x1 = (left - 1) * char_width; + short y1 = (top - 1) * char_height; + short x2 = right * char_width; + short y2 = bottom * char_height; + + _setviewport(x1, y1, x2, y2); + _setcolor(gui_bg_color[color]); + _rectangle(_GFILLINTERIOR, 0, 0, x2 - x1, y2 - y1); +} + +void gui_draw_textwindow(int top, int left, int bottom, int right, gui_color color) +{ + _settextwindow(top, left, bottom, right); + _settextcolor(gui_text_color[color]); // Not really required here + if (vidconf_is_graphics) { + gui_draw_textrectangle(top, left, bottom, right, color); + } else { + _setbkcolor(gui_bg_color[color]); + _clearscreen(_GWINDOW); + } +} + +void gui_draw_title() +{ + const int cols = vidconf.numtextcols; + + gui_draw_textwindow(1, 1, 1, cols, GUI_COLOR_BAR); + gui_print(GUI_COLOR_BAR, "MOUSTEST ESC to exit, 'm'ode, 'r'eset, 's'how, 'h'ide, s'p'eed"); +} + +void gui_draw_status() +{ + const int statusrow = vidconf.numtextrows; + const int cols = vidconf.numtextcols; + + gui_draw_textwindow(statusrow, 1, statusrow, cols, GUI_COLOR_BAR); +} + +void gui_draw_console() +{ + const int rows = vidconf.numtextrows, cols = vidconf.numtextcols; + ctexttop = 3; + ctextbot = rows - 2; + ctextleft = 1; + ctextright = cols; + gui_draw_textwindow(ctexttop, ctextleft, ctextbot, ctextright, GUI_COLOR_CONSOLE); + + ctextpos.row = 1; + ctextpos.col = 1; +} + +void gui_init() +{ + _getvideoconfig(&vidconf); + _gettextsettings(&txtconf); + + _clearscreen(_GCLEARSCREEN); + + switch (vidconf.mode) { + case _TEXTBW40: + case _TEXTC40: + case _TEXTBW80: + case _TEXTC80: + case _TEXTMONO: + vidconf_is_graphics = false; + break; + default: + vidconf_is_graphics = true; + break; + } + + switch (vidconf.mode) { + case _TEXTBW40: + case _TEXTBW80: + case _TEXTMONO: + case _HERCMONO: + case _MRESNOCOLOR: + case _ERESNOCOLOR: + case _VRES2COLOR: + vidconf_is_color = false; + break; + default: + vidconf_is_color = true; + break; + } + + if (vidconf_is_graphics) { + if (vidconf_is_color) { + _remappalette(0, _BLACK); + _remappalette(1, _CYAN); + _remappalette(2, _BLUE); + _remappalette(3, _WHITE); + gui_bg_color[GUI_COLOR_CONSOLE] = 0; + gui_bg_color[GUI_COLOR_BAR] = 3; + gui_bg_color[GUI_COLOR_WINDOW] = 2; + gui_bg_color[GUI_COLOR_LIST] = 1; + gui_bg_color[GUI_COLOR_LIST_SEL] = 3; + gui_text_color[GUI_COLOR_CONSOLE] = 3; + gui_text_color[GUI_COLOR_BAR] = 0; + gui_text_color[GUI_COLOR_WINDOW] = 3; + gui_text_color[GUI_COLOR_LIST] = 0; + gui_text_color[GUI_COLOR_LIST_SEL] = 0; + } else { + gui_bg_color[GUI_COLOR_CONSOLE] = T_BLACK; + gui_bg_color[GUI_COLOR_BAR] = T_WHITE; + gui_bg_color[GUI_COLOR_WINDOW] = T_WHITE; + gui_bg_color[GUI_COLOR_LIST] = T_WHITE; + gui_bg_color[GUI_COLOR_LIST_SEL] = T_BLACK; + gui_text_color[GUI_COLOR_CONSOLE] = T_WHITE; + gui_text_color[GUI_COLOR_BAR] = T_BLACK; + gui_text_color[GUI_COLOR_WINDOW] = T_BLACK; + gui_text_color[GUI_COLOR_LIST] = T_BLACK; + gui_text_color[GUI_COLOR_LIST_SEL] = T_WHITE | T_BRIGHT; + } + } else { + if (vidconf_is_color) { + gui_bg_color[GUI_COLOR_CONSOLE] = T_BLACK; + gui_bg_color[GUI_COLOR_BAR] = T_WHITE; + gui_bg_color[GUI_COLOR_WINDOW] = T_BLUE; + gui_bg_color[GUI_COLOR_LIST] = T_CYAN; + gui_bg_color[GUI_COLOR_LIST_SEL] = T_WHITE; + gui_text_color[GUI_COLOR_CONSOLE] = T_WHITE; + gui_text_color[GUI_COLOR_BAR] = T_BLACK; + gui_text_color[GUI_COLOR_WINDOW] = T_WHITE; + gui_text_color[GUI_COLOR_LIST] = T_BLACK; + gui_text_color[GUI_COLOR_LIST_SEL] = T_BLACK; + } else { + gui_bg_color[GUI_COLOR_CONSOLE] = T_BLACK; + gui_bg_color[GUI_COLOR_BAR] = T_WHITE; + gui_bg_color[GUI_COLOR_WINDOW] = T_WHITE; + gui_bg_color[GUI_COLOR_LIST] = T_WHITE; + gui_bg_color[GUI_COLOR_LIST_SEL] = T_BLACK; + gui_text_color[GUI_COLOR_CONSOLE] = T_WHITE; + gui_text_color[GUI_COLOR_BAR] = T_BLACK; + gui_text_color[GUI_COLOR_WINDOW] = T_BLACK; + gui_text_color[GUI_COLOR_LIST] = T_BLACK; + gui_text_color[GUI_COLOR_LIST_SEL] = T_WHITE | T_BRIGHT; + } + } + + _displaycursor(_GCURSOROFF); + _wrapon(_GWRAPOFF); + + gui_draw_title(); + + gui_draw_status(); + + gui_draw_console(); +} + +void console_enter() +{ + _settextwindow(ctexttop, ctextleft, ctextbot, ctextright); + _settextposition(ctextpos.row, ctextpos.col); + if (!vidconf_is_graphics) _setbkcolor(ctextbg); + _settextcolor(ctextcolor); + _wrapon(_GWRAPON); +} + +void console_leave() +{ + ctextpos = _gettextposition(); + _wrapon(_GWRAPOFF); +} + +void console_print(char __far *str) +{ + _disable(); + int33_hide_cursor(); + console_enter(); + _outtext(str); + console_leave(); + int33_show_cursor(); + _enable(); +} + +void console_printf(const char *format, ...) +{ + va_list arglist; + char buffer[120]; + + va_start(arglist, format); + vsprintf(buffer, format, arglist); + va_end(arglist); + + console_print(buffer); +} + +int console_getline(char *buffer, int maxlen) +{ + int bufpos = 0; + + while (bufpos < maxlen) { + int c = getch(); + switch (c) { + case 0: // Extended: consume and ignore + c = getch(); + break; + case 27: // ESC + console_print("\n"); + return -1; + case '\r': + buffer[bufpos] = '\0'; + console_print("\n"); + return bufpos; + default: + if (isalnum(c) || isspace(c)) { + buffer[bufpos++] = c; + console_printf("%c", c); // Echo + } + } + } + + return bufpos; +} + +struct modeentry { + char *name; + int modenum; +} modelist[] = { + {"TEXTBW40", _TEXTBW40}, + {"TEXTC40", _TEXTC40}, + {"TEXTBW80", _TEXTBW80}, + {"TEXTC80", _TEXTC80}, + {"MRES4COLOR", _MRES4COLOR}, + {"MRESNOCOLOR", _MRESNOCOLOR}, + {"HRESBW", _HRESBW}, + {"TEXTMONO", _TEXTMONO}, + {"HERCMONO", _HERCMONO}, + {"MRES16COLOR", _MRES16COLOR}, + {"HRES16COLOR", _HRES16COLOR}, + {"ERESNOCOLOR", _ERESNOCOLOR}, + {"ERESCOLOR", _ERESCOLOR}, + {"VRES2COLOR", _VRES2COLOR}, + {"VRES16COLOR", _VRES16COLOR}, + {"MRES256COLOR", _MRES256COLOR}, + {"URES256COLOR", _URES256COLOR}, + {"VRES256COLOR", _VRES256COLOR}, + {"SVRES16COLOR", _SVRES16COLOR}, + {"SVRES256COLOR", _SVRES256COLOR}, + {"XRES16COLOR", _XRES16COLOR}, + {"XRES256COLOR", _XRES256COLOR}, +}; +const int num_modeentries = sizeof(modelist)/sizeof(struct modeentry); + +struct { + int first_entry; + int sel_entry; + int list_rows; +} mlistui; + +void modelist_switch_to(int entry) +{ + struct modeentry *mode = &modelist[entry]; + + dprintf(DPREFIX "switching to mode %s %d\n", mode->name, mode->modenum); + + _setvideomode(mode->modenum); +} + +int modelist_get_current() +{ + int i; + + for (i = 0; i < num_modeentries; ++i) { + if (modelist[i].modenum == vidconf.mode) { + return i; + } + } + + return 0; +} + +void modelist_draw_window() +{ + const int list_width = 23; + const int rows = vidconf.numtextrows, cols = vidconf.numtextcols; + int i; + + int33_hide_cursor(); + + gui_draw_textwindow(3, 4, rows - 3, list_width + 1, GUI_COLOR_WINDOW); + gui_print(GUI_COLOR_WINDOW, " Choose video mode: "); + + int33_show_cursor(); +} + +void modelist_draw_list() +{ + const int list_width = 23; + const int rows = vidconf.numtextrows, cols = vidconf.numtextcols; + int i; + + int33_hide_cursor(); + + gui_draw_textwindow(5, 5, 5 + mlistui.list_rows - 1, list_width, GUI_COLOR_LIST); + + for (i = mlistui.first_entry; i < MIN(num_modeentries, mlistui.first_entry + mlistui.list_rows); ++i) { + const bool selected = i == mlistui.sel_entry; + _settextposition(1 + (i - mlistui.first_entry), 1); + gui_print(selected ? GUI_COLOR_LIST_SEL : GUI_COLOR_LIST, modelist[i].name); + } + + if (mlistui.first_entry > 0) { + _settextposition(1, list_width); + gui_print(GUI_COLOR_LIST, "\x18"); + } + if (mlistui.first_entry + mlistui.list_rows < num_modeentries) { + _settextposition(1 + mlistui.list_rows, list_width); + gui_print(GUI_COLOR_LIST, "\x19"); + } + + int33_show_cursor(); +} + +bool modelist_sel_entry(int offset) +{ + int new_sel_entry = mlistui.sel_entry + offset; + if (new_sel_entry < 0) { + new_sel_entry = 0; + } else if (new_sel_entry >= num_modeentries) { + new_sel_entry = num_modeentries - 1; + } + if (new_sel_entry != mlistui.sel_entry) { + mlistui.sel_entry = new_sel_entry; + if (mlistui.sel_entry < mlistui.first_entry) { + mlistui.first_entry = mlistui.sel_entry; + } else if (mlistui.sel_entry >= mlistui.first_entry + mlistui.list_rows) { + mlistui.first_entry = 1 + mlistui.sel_entry - mlistui.list_rows; + } + return true; + } else { + return false; + } +} + +void modelist_show_modal() +{ + bool exiting = false; + + // TODO Mouse support here :) + //int33_set_event_handler(INT33_EVENT_MASK_ALL, modelist_mouse_callback); + int33_set_event_handler(0, NULL); + int33_hide_cursor(); + + mlistui.first_entry = 0; + mlistui.sel_entry = 0; + mlistui.list_rows = vidconf.numtextrows - 8; + + modelist_sel_entry(modelist_get_current()); + + modelist_draw_window(); + modelist_draw_list(); + + while (!exiting) { + int c = getch(); + + dprintf(DPREFIX "modelist getch returns %d\n", c); + + switch (c) { + case 27: // Escape + exiting = true; + break; + case '\r': + modelist_switch_to(mlistui.sel_entry); + exiting = true; + break; + case 0: + c = getch(); + + dprintf(DPREFIX "modelist getch returns extended %d\n", c); + + switch (c) { + case 72: // Arrow-Up + if (modelist_sel_entry(-1)) { + modelist_draw_list(); + } + break; + case 80: // Arrow-Down + if (modelist_sel_entry(+1)) { + modelist_draw_list(); + } + break; + case 73: // Page-Up + if (modelist_sel_entry(-mlistui.list_rows)) { + modelist_draw_list(); + } + break; + case 81: // Page-Down + if (modelist_sel_entry(+mlistui.list_rows)) { + modelist_draw_list(); + } + break; + } + break; + } + } + + int33_set_event_handler(0, NULL); +} + +/** Called by the int33 mouse driver. */ +void __far 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) mouse_callback +{ + _disable(); + +#if 1 + dprintf(DPREFIX "mouse_callback events=0x%x buttons=0x%x x=%d y=%d dx=%d dy=%d\n", + events, buttons, x, y, delta_x, delta_y); +#endif + + lastmouseevent.events = events; + lastmouseevent.buttons = buttons & 0xFFU; + lastmouseevent.x = x; + lastmouseevent.y = y; + lastmouseevent.z = buttons >> 8; + lastmouseevent.delta_x = delta_x; + lastmouseevent.delta_y = delta_y; + + // Doing this to wake up getch() + int16_store_keystroke(1, 0); + + _enable(); +} + +bool mouse_reset() +{ + if (!int33_reset_get_buttons(&num_buttons)) { + puts("Mouse not installed"); + return false; + } + + if (!args_no_wheel) { + driver_caps = int33_get_capabilities(); + num_wheels = 0; + if (driver_caps & INT33_CAPABILITY_WHEEL_API) { + num_wheels = 1; + } + if (driver_caps & INT33_CAPABILITY_WHEEL2_API) { + num_wheels = 2; + } + } + + int33_set_event_handler(INT33_EVENT_MASK_ALL, mouse_callback); + + return true; +} + +void mouse_report() +{ + const char __far *fstr; + + if (driver_caps != 0) { + console_printf("Driver capabilities bits: 0x%x %s %s\n", driver_caps, + driver_caps & INT33_CAPABILITY_WHEEL_API ? "<wheel API>" : "", + driver_caps & INT33_CAPABILITY_WHEEL2_API ? "<wheel API v2>" : ""); + } + + fstr = int33_get_version_string(); + if (fstr) { + console_printf("Driver version string: %Fs\n", fstr); + } + + console_printf("Number of buttons: %u\n", num_buttons); + console_printf("Number of wheels: %u\n", num_wheels); +} + +void mouse_speed() +{ + uint16_t speed_x, speed_y, double_speed_threshold; + bool update = false; + char buffer[16] = {'\0'}; + int ret; + + int33_get_sensitivity(&speed_x, &speed_y, &double_speed_threshold); + + console_printf("X speed [default=8, current=%u] ? ", speed_x); + ret = console_getline(buffer, sizeof(buffer)-1); + if (ret > 0) { + speed_x = atoi(buffer); + update = true; + } + + console_printf("Y speed [default=16, current=%u] ? ", speed_y); + ret = console_getline(buffer, sizeof(buffer)-1); + if (ret > 0) { + speed_y = atoi(buffer); + update = true; + } + + console_printf("Double speed threshold [default=64, current=%u] ? ", double_speed_threshold); + ret = console_getline(buffer, sizeof(buffer)-1); + if (ret > 0) { + double_speed_threshold = atoi(buffer); + update = true; + } + + if (update) { + int33_set_sensitivity(speed_x, speed_y, double_speed_threshold); + console_printf("Values updated: %u %u %u\n", speed_x, speed_y, double_speed_threshold); + } else { + console_printf("No values changed\n"); + } +} + +void mouse_quit() +{ + int33_reset(); +} + +void mouse_debug() +{ + // Add test code here +} + +void status_report_last_event() +{ + uint16_t events = lastmouseevent.events; + uint8_t buttons = lastmouseevent.buttons; + int x = lastmouseevent.x; + int y = lastmouseevent.y; + int z = lastmouseevent.z; + lastmouseevent.events = 0; + + if (!events) { + return; + } + + // Update the status bar message + gui_draw_status(); + + gui_printf(GUI_COLOR_BAR, " %3s %4u , %4u |", + events & INT33_EVENT_MASK_ABSOLUTE ? "ABS" : "REL", x, y); + + + if (num_buttons <= 2) { + gui_printf(GUI_COLOR_BAR, " [%5s] [%5s] ", + buttons & INT33_BUTTON_MASK_LEFT ? "LEFT" : "", + buttons & INT33_BUTTON_MASK_RIGHT ? "RIGHT" : ""); + } else { + gui_printf(GUI_COLOR_BAR, " [%5s] [%5s] [%5s] ", + buttons & INT33_BUTTON_MASK_LEFT ? "LEFT" : "", + buttons & INT33_BUTTON_MASK_CENTER ? "MID " : "", + buttons & INT33_BUTTON_MASK_RIGHT ? "RIGHT" : ""); + if (num_buttons > 3) { + gui_printf(GUI_COLOR_BAR, " [%3s] [%3s] ", + buttons & INT33_BUTTON_MASK_4TH ? "4TH" : "", + buttons & INT33_BUTTON_MASK_5TH ? "5TH" : ""); + } + } + + if (num_wheels > 0) { + char c = ' '; + if (events & INT33_EVENT_MASK_WHEEL_MOVEMENT) { + c = z > 0 ? 0x19 : 0x18; + } else if (events & INT33_EVENT_MASK_HORIZ_WHEEL_MOVEMENT) { + c = z > 0 ? 0x1A : 0x1B; + } + gui_printf(GUI_COLOR_BAR, "| %c", c); + } + + // Log the events to the console + if (events & INT33_EVENT_MASK_LEFT_BUTTON_PRESSED) { + console_printf("Left button pressed\n"); + } + if (events & INT33_EVENT_MASK_LEFT_BUTTON_RELEASED) { + console_printf("Left button released\n"); + } + if (events & INT33_EVENT_MASK_CENTER_BUTTON_PRESSED) { + console_printf("Middle button pressed\n"); + } + if (events & INT33_EVENT_MASK_CENTER_BUTTON_RELEASED) { + console_printf("Middle button released\n"); + } + if (events & INT33_EVENT_MASK_RIGHT_BUTTON_PRESSED) { + console_printf("Right button pressed\n"); + } + if (events & INT33_EVENT_MASK_RIGHT_BUTTON_RELEASED) { + console_printf("Right button released\n"); + } + if (events & INT33_EVENT_MASK_4TH_BUTTON_PRESSED) { + console_printf("4th button pressed\n"); + } + if (events & INT33_EVENT_MASK_4TH_BUTTON_RELEASED) { + console_printf("4th button released\n"); + } + if (events & INT33_EVENT_MASK_5TH_BUTTON_PRESSED) { + console_printf("5th button pressed\n"); + } + if (events & INT33_EVENT_MASK_5TH_BUTTON_RELEASED) { + console_printf("5th button released\n"); + } + + if (events & INT33_EVENT_MASK_WHEEL_MOVEMENT) { + console_printf("Wheel %s %d\n", z > 0 ? "down" : "up", z); + } + if (events & INT33_EVENT_MASK_HORIZ_WHEEL_MOVEMENT) { + console_printf("Wheel %s %d\n", z > 0 ? "right" : "left", z); + } +} + +int main(int argc, const char *argv[]) +{ + int i; + for (i = 1; i < argc; i++) { + if (stricmp(argv[i], "/nr") == 0) { + args_no_reset = true; + } else if (stricmp(argv[i], "/nw") == 0) { + args_no_wheel = true; + } + } + + gui_init(); + + if (!args_no_reset) { + if (mouse_reset()) { + mouse_report(); + int33_show_cursor(); + } else { + console_printf("Mouse driver not found or failed to reset\n"); + } + } else { + console_printf("Use r, s to reset and show mouse pointer\n"); + } + + while (!main_exiting) { + int c = getch(); + + dprintf(DPREFIX "getch returns %d\n", c); + + if (c == 0) { + // Extended key + c = getch(); + + dprintf(DPREFIX "getch returns extended %d\n", c); + + switch (c) { + case 1: // Internal: mouse event + status_report_last_event(); + break; + default: + console_printf("Keyboard extended key %d\n", c); + break; + } + } else { + console_printf("Keyboard key %d '%c'\n", c, c); + + switch (c) { + case 27: // Escape + main_exiting = true; + break; + case 'r': + console_printf("Reset mouse\n"); + if (mouse_reset()) { + mouse_report(); + console_printf("Mouse reset complete\n"); + } else { + console_printf("Mouse reset failed\n"); + } + break; + case 's': + int33_show_cursor(); + break; + case 'h': + int33_hide_cursor(); + break; + case 'm': + modelist_show_modal(); + gui_init(); + if (!args_no_reset) { + if (mouse_reset()) { + mouse_report(); + int33_show_cursor(); + console_printf("Mouse reset complete\n"); + } else { + console_printf("Mouse reset failed\n"); + } + } + break; + case 'p': + mouse_speed(); + case 'd': + mouse_debug(); + break; + case 'c': + _clearscreen(_GCLEARSCREEN); + break; + } + } + } + + mouse_quit(); + _setvideomode(_DEFAULTMODE); + + return EXIT_SUCCESS; +} diff --git a/nls/.gitattributes b/nls/.gitattributes new file mode 100644 index 0000000..8d18639 --- /dev/null +++ b/nls/.gitattributes @@ -0,0 +1,2 @@ +vbmouse.* text eol=crlf +vbsf.* text eol=crlf diff --git a/nls/vbmouse.en b/nls/vbmouse.en index 3febaba..1a5aa9f 100644 --- a/nls/vbmouse.en +++ b/nls/vbmouse.en @@ -9,14 +9,18 @@ 0.2:Supported actions and options: 0.3: install Install the driver (default). 0.4: low Install in conventional memory (otherwise UMB). -0.5: uninstall Uninstall the driver from memory. -0.6: wheel <ON|OFF> Enable/disable wheel API support. -0.7: wheelkey <KEY|OFF> Emulate a specific keystroke on wheel scroll. -0.8: Supported keys: updn, pageupdn. -0.9: integ <ON|OFF> Enable/disable VirtualBox integration. -0.10: hostcur <ON|OFF> Enable/disable mouse cursor rendering in the host. -0.11: reset Reset mouse driver. -1.0:Wheel mouse found and enabled\n +0.5: ps2, com1, com2, ... Specify on which ports to search a mouse. +0.6: uninstall Uninstall the driver from memory. +0.7: rescan <ps2|com1|..> Rescan for mouse in specified ports. +0.8: wheel <ON|OFF> Enable/disable wheel API support. +0.9: wheelkey <KEY|OFF> Emulate a specific keystroke on wheel scroll. +0.10: Supported keys: updn, pageupdn. +0.11: hwheelkey <KEY|OFF> Likewise for horizontal wheel scroll. +0.12: Supported keys: lr. +0.13: integ <ON|OFF> Enable/disable VirtualBox integration. +0.14: hostcur <ON|OFF> Enable/disable mouse cursor rendering in the host. +0.15: reset Reset mouse driver. +1.0:Found PS/2 mouse with %u buttons, %u wheels\n 1.1:Setting wheel support to %s\n 1.2:enabled 1.3:disabled @@ -38,6 +42,7 @@ 1.19:Reset mouse driver\n 1.20:\nVBMouse %x.%x (like MSMOUSE %x.%x)\n 1.21:VBMouse already installed\n +1.22:Found serial mouse on COM%u with %u buttons, %u wheels\n 3.0:Could not find PS/2 wheel mouse\n 3.1:Wheel not detected or support not enabled\n 3.2:Unknown key '%s'\n @@ -47,8 +52,9 @@ 3.6:Could not detect VMware, err=%ld\n 3.7:VMware absolute pointer error, err=0x%lx\n 3.8:Cannot init PS/2 mouse BIOS, err=%d\n -3.9:INT33 has been hooked by someone else, cannot safely remove\n -3.10:INT2F has been hooked by someone else, cannot safely remove\n +3.9:INT%X has been hooked by someone else, cannot safely remove\n 3.11:Driver data not found (driver not installed?)\n 3.12:Invalid argument '%s'\n 3.13:Argument required for '%s'\n +3.14:No mouse found\n +3.15:Cannot find mouse in COM%u, err=%d\n diff --git a/nls/vbmouse.es b/nls/vbmouse.es index 7b79bda..924ff59 100644 --- a/nls/vbmouse.es +++ b/nls/vbmouse.es @@ -11,14 +11,18 @@ 0.2:Acciones y opciones soportadas: 0.3: install Instala el controlador (por defecto). 0.4: low Instala en memoria convencional (UMB si no). -0.5: uninstall Desinstala el controlador de la memoria. -0.6: wheel <ON|OFF> Habilita/deshabilita el soporte para la rueda. -0.7: wheelkey <TECLA|OFF> Emula una tecla especĦfica al rotar la rueda. -0.8: Teclas soportadas: updn, pageupdn. -0.9: integ <ON|OFF> Habilita/deshabilita integraci˘n con VirtualBox. -0.10: hostcur <ON|OFF> Habilita/deshabilita cursor dibujado por anfitri˘n. -0.11: reset Reinicia el controlador del rat˘n. -1.0:Rueda de rat˘n encontrada y activada\n +0.5: ps2, com1, com2, ... Especifica los puertos donde buscar el rat˘n. +0.6: uninstall Desinstala el controlador de la memoria. +0.7: rescan <ps2|com1|..> Vuelve a buscar rat˘n en los puertos especificados. +0.8: wheel <ON|OFF> Habilita/deshabilita el soporte para la rueda. +0.9: wheelkey <TECLA|OFF> Emula una tecla especĦfica al rotar la rueda. +0.10: Teclas soportadas: updn, pageupdn. +0.11: hwheelkey <TECLA|OFF> Lo mismo pero para la rueda horizontal. +0.12: Teclas soportadas: lr. +0.13: integ <ON|OFF> Habilita/deshabilita integraci˘n con VirtualBox. +0.14: hostcur <ON|OFF> Habilita/deshabilita cursor dibujado por anfitri˘n. +0.15: reset Reinicia el controlador del rat˘n. +1.0:Encontrado rat˘n PS/2 con %u botones, %u ruedas\n 1.1:Soporte para rueda %s\n 1.2:habilitado 1.3:deshabilitado @@ -40,6 +44,7 @@ 1.19:Reiniciados ajustes del controlador del rat˘n\n 1.20:\nVBMouse %x.%x (como MSMOUSE %x.%x)\n 1.21:VBMouse ya instalado\n +1.22:Encontrado rat˘n serie en COM%u con %u botones, %u ruedas\n 3.0:No se pudo encontrar rat˘n PS/2 con rueda\n 3.1:Rueda no detectada o soporte no habilitado\n 3.2:Tecla desconocida '%s'\n @@ -49,8 +54,9 @@ 3.6:No se pudo detectar VMware, err=%ld\n 3.7:Error al habilitar dispositivo apuntador absoluto en VMware, err=0x%lx\n 3.8:No puedo iniciar la BIOS del rat˘n PS/2, err=%d\n -3.9:Alguien m s enganchado a INT33, no puedo desinstalar de forma segura\n -3.10:Alguien m s enganchado a INT2F, no puedo desinstalar de forma segura\n +3.9:Alguien m s enganchado a INT%X, no puedo desinstalar de forma segura\n 3.11:No encuentro los datos del controlador (¨No est instalado?)\n 3.12:Argumento no v lido '%s'\n 3.13:Se requiere argumento para '%s'\n +3.14:Ning£n rat˘n encontrado\n +3.15:No se pudo encontrar rat˘n en COM%u, err=%d\n diff --git a/nls/vbmouse.fr b/nls/vbmouse.fr new file mode 100644 index 0000000..95f0d95 --- /dev/null +++ b/nls/vbmouse.fr @@ -0,0 +1,61 @@ +# Language: French +# Codepage: 850 +# +# Spaces before text must be kept. Be sure that no spaces are +# added to the end of the lines. +# +0.0:Utilisation : +0.1: VBMOUSE <ACTION> <ARGS..> +0.2:Actions et options prises en charge : +0.3: install Installer le pilote (valeur par dfaut). +0.4: low dans mmoire conventionnelle (sinon UMB). +0.5: ps2, com1, com2, ... sur quels ports rechercher une souris. +0.6: uninstall Dsinstaller le pilote de la mmoire. +0.7: rescan <ps2|com1|..> Chercher la souris
nouveau sur les ports indiqus. +0.8: wheel <ON|OFF> Activer/dsactiver le support de l'API roulette. +0.9: wheelkey <TOUCHE|OFF> muler un appui lors du dfilement roulette. +0.10: Pris en charge: updn, pageupdn. +0.11: hwheelkey <TOUCHE|OFF> Likewise for horizontal wheel scroll. +0.12: Pris en charge: lr. +0.13: integ <ON|OFF> Activer/dsactiver l'intgration VirtualBox. +0.14: hostcur <ON|OFF> Activer/dsactiver rendu du curseur par l'hte. +0.15: reset Rinitialiser le pilote de la souris. +1.0:Souris PS/2 trouve avec %u boutons, %u roulettes\n +1.1:Rglage de la prise en charge de la roulette sur %s\n +1.2:activ +1.3:dsactiv +1.4:Gnrer Curseur haut / Curseur bas lors des mouvements de la roulette\n +1.5:Gnrer Page haute / Page basse lors des mouvements de la roulette\n +1.6:Dsactivation de la gnration d'appuis de touches avec le roulette\n +1.7:Intgration VirtualBox active\n +1.8:Intgration VirtualBox dsactive\n +1.9:L'intgration VirtualBox dj
dsactive ou bien non disponible\n +1.10:Rglage du curseur de l'hte sur %s\n +1.11:Protocole VMware version %ld trouv\n +1.12:Intgration VMware active\n +1.13:Intgration VMware dsactive\n +1.14:L'intgration VMware dj
dsactive ou bien non disponible\n +1.15:Ni l'intgration VirtualBox ni celle de VMware ne sont disponibles\n +1.16:L'intgration VirtualBox n'est pas disponible\n +1.17:Pilote install\n +1.18:Pilote dsinstall\n +1.19:Pilote de souris rinitialis\n +1.20:\nVBMouse %x.%x (similaire
MSMOUSE %x.%x)\n +1.21:VBMouse dj
install\n +1.22:Souris srie trouve sur COM%u avec %u boutons, %u roulettes\n +3.0:Souris PS/2
roulette introuvable\n +3.1:Roulette non dtecte ou bien prise en charge non active\n +3.2:Touche inconnue '%s'\n +3.3:Impossible de trouver le priphrique VirtualBox PCI, err=%d\n +3.4:Impossible de verrouiller le tampon pour la comm. VirtualBox, err=%d\n +3.5:La communication avec VirtualBox ne fonctionne pas, err=%d\n +3.6:Impossible de dtecter VMware, err=%ld\n +3.7:Erreur de pointeur absolu de VMware, err=0x%lx\n +3.8:Impossible d'initialiser le BIOS de souris PS/2, err=%d\n +3.9:INT33 intercept par quelqu'un d'autre, impossible de l'enlever de faon sre\n +3.10:INT2F intercept par quelqu'un d'autre, impossible de l'enlever de faon sre\n +3.11:Les donnes du pilote sont introuvables (pilote non install ?)\n +3.12:Argument invalide '%s'\n +3.13:Argument ncessaire pour '%s'\n +3.14:Aucune souris trouve\n +3.15:Impossible de trouver souris sur COM%u, err=%d\n diff --git a/nls/vbmouse.tr b/nls/vbmouse.tr new file mode 100644 index 0000000..9943ae2 --- /dev/null +++ b/nls/vbmouse.tr @@ -0,0 +1,61 @@ +# Language: Turkish +# Codepage: 857 +# +# Spaces before text must be kept. Be sure that no spaces are +# added to the end of the lines. +# +0.0:Kullanm: +0.1: VBMOUSE <EYLEM> <ARGMANLAR..> +0.2:Desteklenen eylem ve seenekler: +0.3: install Src yazlmn kur (varsaylan). +0.4: low Geleneksel belle§e ykle (yoksa UMB). +0.5: ps2, com1, com2, ... Specify on which ports to search a mouse. +0.6: uninstall Src yazlmn bellekten kaldr. +0.7: rescan <ps2|com1|..> Rescan for mouse in specified ports. +0.8: wheel <ON|OFF> Tekerlek API deste§ini etkinletir/devre d brak. +0.9: wheelkey <TU|OFF> Tekerle§i kaydrmada belli bir tu basmn taklit +0.10: et. Desteklenen tular: updn, pageupdn +0.11: hwheelkey <KEY|OFF> Likewise for horizontal wheel scroll. +0.12: Supported keys: lr. +0.13: integ <ON|OFF> VirtualBox birleimini etkinletir/deve d brak. +0.14: hostcur <ON|OFF> Fare imleci grntlenmesini etkinletir/devre d. +0.15: reset Fare srcsn sfrla. +1.0:Found PS/2 mouse with %u buttons, %u wheels\n +1.1:Tekerlek deste§i %s olarak ayarlanyor\n +1.2:etkinletirilmi +1.3:devre d +1.4:Tekerlek hareketinde Yukar mle / Aa§ mle tu basmlarn olutur\n +1.5:Tekerlek hareketinde Yukar Sayfa / Aa§ Sayfa tu basmlarn olutur\n +1.6:Tekerlek ile tu basm oluturulmas devre d braklyor\n +1.7:VirtualBox birleimi etkinletirildi\n +1.8:VirtualBox birleimi devre d brakld\n +1.9:VirtualBox birleimi zaten devre d veya mevcut de§il\n +1.10:Ev sahibi bilgisayardaki imle %s olarak ayarlanyor\n +1.11:VMware protokol srm %ld bulundu\n +1.12:VMware birleimi etkinletirildi\n +1.13:VMware birleimi devre d brakld\n +1.14:VMware birleimi zaten devre d veya mevcut de§il\n +1.15:Ne VirtualBox ne de VMware birleimi mevcut de§il\n +1.16:VirtualBox birleimi mevcut de§il\n +1.17:Src kuruldu\n +1.18:Src kaldrld\n +1.19:Fare srcs sfrland\n +1.20:\nVBMouse %x.%x (MSMOUSE %x.%x gibi)\n +1.21:VBMouse zaten kurulu\n +1.22:Found serial mouse on COM%u with %u buttons, %u wheels\n +3.0:PS/2 tekerlekli fare bulunamad\n +3.1:Tekerlek tespit edilemedi veya destek etkinletirilmemi\n +3.2:Bilinmeyen tu '%s'\n +3.3:VirtualBox PCI cihaz bulunamaz, hata=%d\n +3.4:VirtualBox iletiimi iin kullanlan tampon kilitlenemez, hata=%d\n +3.5:VirtualBox iletiimi almyor, hata=%d\n +3.6:VMware tespit edilemedi, hata=%ld\n +3.7:VMware mutlak fare imle hatas, hata=0x%lx\n +3.8:PS//2 fare BIOS balatlamaz, hata=%d\n +3.9:INT33 bakas tarafndan ele geirilmi, gvenli bir ekilde kaldrlamaz\n +3.10:INT2F bakas tarafndan ele geirilmi, gvenli bir ekilde kaldrlamaz\n +3.11:Src yazlm verileri bulunamad (src yazlm kurulu de§il mi?)\n +3.12:Geersiz argman '%s'\n +3.13:'%s' iin argman gerekli\n +3.14:No mouse found\n +3.15:Cannot find mouse in COM%u, err=%d\n diff --git a/nls/vbsf.fr b/nls/vbsf.fr new file mode 100644 index 0000000..c98fc07 --- /dev/null +++ b/nls/vbsf.fr @@ -0,0 +1,60 @@ +# Language: French +# Codepage: 850 +# +# Spaces before text must be kept. Be sure that no spaces are +# added to the end of the lines. +# +0.0:Utilisation : +0.1: VBSF <ACTION> <ARGS..> [<OPTIONS..>] +0.2:Action et options prises en charge : +0.3: install Installer le pilote (valeur par dfaut). +0.4: low dans mmoire conventionnelle (sinon UMB). +0.5: uninstall Dsinstaller le pilote de la mmoire. +0.6: list Lister les rpertoires partags disponibles. +0.7: mount <DOSSIER> <X:> ... Monter un dossier partag sur le lecteur X:. +0.8: /hash <n> Nombre de caractres de hachage suivant le '~' +0.9: pour les noms de fichiers courts DOS gnrs. +0.10: (entre %d et %d; par dfaut %d; 0 dsactiver)\n +0.11: /host Utiliser les noms courts depuis les htes Win. +0.12: /upper Exiger des noms de fichiers htes en majuscules. +0.13: remount <X:> ... Modifier options de montage pour lecteur X:. +0.14: umount <X:> Dmonter le dossier partag du lecteur X:. +0.15: rescan Dmonter tout et recrer des montage automatiques. +1.0:Lecteurs monts :\n +1.1: %s sur %c: %s\n +1.2:Dossiers partags disponibles :\n +1.3:Dossier partag '%s' mont en tant que lecteur %c:\n +1.4:Lecteur %c: dmont\n +1.5:Utilisation du fuseau horaire depuis la variable TZ (%s)\n +1.6:Connect au service de dossiers partags de VirtualBox\n +1.7:Pilote install\n +1.8:Pilote dsinstall\n +1.9:\nVBSharedFolders %x.%x\n +1.10:VBSF dj
install\n +2.0:Avertissement : la page de code active est introuvable +2.1:Avertissement : impossible de trouver la table Unicode : %s +2.2:Avertissement : impossible de charger la table Unicode : %s +2.3:Avertissement : format de fichier invalide : %s +2.4:. Utilisation de cp437 par dfaut\n +3.0:Erreur lors de Query Map Name, (requte de nom de mappage) err=%ld\n +3.1:Erreur lors de Query Mappings (mappage des requtes), err=%ld\n +3.2:Erreur lors de Close File (fermeture de fichier), err=%ld\n +3.3:Impossible de monter le rpertoire partag '%s', err=%d\n +3.4:Erreur lors de Query Map Info pour le lecteur mont, err=%ld\n +3.5:Impossible de dmonter le rpertoire partag, err=%d\n +3.6:Lecteur invalide %c:\n +3.7:Le lecteur %c: est aprs LASTDRIVE (dernier lecteur)\n +3.8:Lecteur %c dj
mont\n +3.9:Lecteur %c : existe dj
\n +3.11:Lecteur %c non mont\n +3.12:Erreur lors de Query Map Info +3.13:Impossible d'obtenir les tables NLS +3.14:Impossible de trouver le priphrique VirtualBox PCI, err=%ld\n +3.15:Impossible de verrouiller le tampon pour la comm. VirtualBox, err=%ld\n +3.16:La communication VirtualBox ne fonctionne pas, err=%ld\n +3.17:Impossible de se connecter au service de dossiers partags, err=%ld\n +3.18:Impossible de configurer UTF-8 sur le service de dossiers partags, err=%ld\n +3.19:INT2F intercept par quelqu'un d'autre, impossible de l'enlever de faon sre\n +3.20:Les donnes du pilote sont introuvables (pilote non install ?)\n +3.21:Argument invalide '%s'\n +3.22:Argument ncessaire pour '%s'\n diff --git a/nls/vbsf.tr b/nls/vbsf.tr new file mode 100644 index 0000000..56964d5 --- /dev/null +++ b/nls/vbsf.tr @@ -0,0 +1,60 @@ +# Language: Turkish +# Codepage: 857 +# +# Spaces before text must be kept. Be sure that no spaces are +# added to the end of the lines. +# +0.0:Kullanm: +0.1: VBSF <EYLEM> <ARGMANLAR..> [<SEENEKLER..>] +0.2:Desteklenen eylem ve seenekler: +0.3: install Srcy kur (varsaylan). +0.4: low Geleneksel belle§e ykle (yoksa UMB). +0.5: uninstall Src yazlmn bellekten kaldr. +0.6: list Mevcut paylalm dizinleri listele. +0.7: mount <FOLD> <X:> ... X: srcsne paylalm bir dosya ba§la. +0.8: /hash <n> '~' iaretini takip eden karma karakter says, +0.9: oluturulmu DOS ksa dosya isimleri iin. +0.10: (%d ve %d aras; varsaylan %d; 0 devre d)\n +0.11: /host Windows bilgisayarndan ksa doysa adlar kullan. +0.12: /upper Byk harfli dosya adlar talep et. +0.13: remount <X:> ... Ba§l src X: iin ba§lama seeneklerini de§itir. +0.14: umount <X:> X: srcsnden paylalm dizin ba§n kaldr. +0.15: rescan Tm ba§lar kaldr ve tekrar otomatik olutur. +1.0:Ba§l srcler:\n +1.1: %s, %c zerinde: %s\n +1.2:Mevcut paylalm dizinler:\n +1.3:'%s' paylalm dizini %c: srcs olarak ba§land\n +1.4:%c: srcsnn ba§ kaldrld\n +1.5:Saat dilimi TZ de§ikeninden kullanlyor (%s)\n +1.6:VirtualBox paylalm dizin hizmetine ba§lanld\n +1.7:Src yazlm kuruldu\n +1.8:Src yazlm kaldrld\n +1.9:\nVBSharedFolders %x.%x\n +1.10:VBSF zaten kurulu\n +2.0:kaz: faal kod sayfas bulunamad +2.1:kaz: Unicode tablosu bulunamad: %s +2.2:kaz: Unicode tablosu yklenemedi: %s +2.3:kaz: geersiz dosya biimi: %s +2.4:. Varsaylan olarak cp437 kullanlyor\n +3.0:Query Map Name esnasnda hata, hata=%ld\n +3.1:Query Mappings esnasnda hata, hata=%ld\n +3.2:Dosya kapatmas esnasnda hata, hata=%ld\n +3.3:Paylalm dizin '%s' ba§lanamaz, hata=%d\n +3.4:Ba§l src iin Query Map Info esnasnda hata, hata=%ld\n +3.5:Paylalm dizinin ba§ kaldrlamaz, hata=%d\n +3.6:Geersiz src %c:\n +3.7:Src %c: LASTDRIVE (son srcden) sonra\n +3.8:Src %c zaten ba§l\n +3.9:Src %c: zaten mevcut\n +3.11:Src %c ba§l de§il\n +3.12:Error en Query Map Info +3.13:NLS tablolar alnamaz +3.14:VirtualBox PCI cihaz bulunamad, hata=%ld\n +3.15:VirtualBox iletiimi iin kullanlan tampon kilitlenemez, hata=%ld\n +3.16:VirtualBox iletiimi almyor, hata=%ld\n +3.17:Paylalm dizin hizmetine ba§lanlamaz, hata=%ld\n +3.18:UTF-8, paylalm dizin hizmetinde yaplandrlamaz, hata=%ld\n +3.19:INT2F bakas tarafndan ele geirilmi, gvenli bir ekilde kaldrlamaz\n +3.20:Src yazlm verileri bulunamad (src yazlm kurulu de§il mi?)\n +3.21:Geersiz argman '%s'\n +3.22:'%s' iin argman gerekli\n @@ -0,0 +1,59 @@ +/* + * VBMouse - Programmable Interrupt Controller 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 PIC_H +#define PIC_H + +#include <conio.h> + +// TODO Currently do not support secondary PIC + +enum { + PIC1_CMD_IOPORT = 0x20, + PIC1_DATA_IOPORT = PIC1_CMD_IOPORT+1, + PIC2_CMD_IOPORT = 0xA0, + PIC2_DATA_IOPORT = PIC2_CMD_IOPORT+1, +}; + +enum PIC_CMDS { + /** End-Of-Interrupt command. */ + PIC_CMD_EOI = 0x20 +}; + +static void pic_mask_irq(unsigned irq) +{ + uint8_t mask = inp(PIC1_DATA_IOPORT); + mask |= (1 << irq); + outp(PIC1_DATA_IOPORT, mask); +} + +static void pic_unmask_irq(unsigned irq) +{ + uint8_t mask = inp(PIC1_DATA_IOPORT); + mask &= ~(1 << irq); + outp(PIC1_DATA_IOPORT, mask); +} + +static void pic_eoi_irq(unsigned irq) +{ + (void) irq; + outp(PIC1_CMD_IOPORT, PIC_CMD_EOI); +} + +#endif // PIC_H diff --git a/serial.h b/serial.h new file mode 100644 index 0000000..1ddc317 --- /dev/null +++ b/serial.h @@ -0,0 +1,291 @@ +#ifndef SERIAL_H +#define SERIAL_H + +#include <stdint.h> +#include <conio.h> + +#include "bda.h" + +enum SERIAL_UART_REGS { + SERIAL_TX = 0, + SERIAL_RX = 0, + /** (only with DLAB enabled) LSB of divisor/baud rate */ + SERIAL_DIVISOR_LO = 0, + /** (only with DLAB enabled) MSB of divisor/baud rate */ + SERIAL_DIVISOR_HI = 1, + SERIAL_DIVISOR = SERIAL_DIVISOR_LO, + /** Interrupt Enable Register(read/write) */ + SERIAL_IER = 1, + /** Fifo Control Register (write-only) */ + SERIAL_FCR = 2, + /** Interrupt Identification Register (read-only) */ + SERIAL_IIR = 2, + /** Line Control Register (read/write), contains DLAB bit */ + SERIAL_LCR = 3, + /** Modem Control Register (read/write) */ + SERIAL_MCR = 4, + /** Line Status Register (read-only) */ + SERIAL_LSR = 5, + /** Modem Status Register (read-only) */ + SERIAL_MSR = 6 +}; + +#if 0 +// Comes straight from https://wiki.osdev.org/Serial_Ports#Initialization +outp(DLOG_TARGET_PORT + 1, 0x00); // Disable all interrupts +outp(DLOG_TARGET_PORT + 3, 0x80); // Enable DLAB (set baud rate divisor) +outp(DLOG_TARGET_PORT + 0, 0x01); // Set divisor to 1 (lo byte) 115200 baud +outp(DLOG_TARGET_PORT + 1, 0x00); // (hi byte) +outp(DLOG_TARGET_PORT + 3, 0x03); // 8 bits, no parity, one stop bit +outp(DLOG_TARGET_PORT + 2, 0xC7); // Enable FIFO, clear them, with 14-byte threshold +outp(DLOG_TARGET_PORT + 4, 0x03); // RTS/DSR set, IRQs disabled +#endif + +enum SERIAL_DIVISOR { + SERIAL_DIVISOR_115200 = 1, + SEIRAL_DIVISOR_9600 = 12, + SERIAL_DIVISOR_1200 = 96 +}; + +enum SERIAL_IER { + /** No interrupts */ + SERIAL_IER_NONE = 0, + /** Enable Receiver Buffer Full Interrupt */ + SERIAL_IER_ERBFI = 1 << 0, + /** Enable Transmitter Buffer Empty Interrupt */ + SERIAL_IER_ETBEI = 1 << 1, + /** Enable Line Status Interrupt */ + SERIAL_IER_ELSI = 1 << 2, + /** Enable Delta Status Signals Interrupt */ + SERIAL_IER_EDSSI = 1 << 3, +}; + +enum SERIAL_FCR { + SERIAL_FCR_RX_TRIGER_1 = 0 << 6, + SERIAL_FCR_RX_TRIGER_4 = 1 << 6, + SERIAL_FCR_RX_TRIGER_8 = 2 << 6, + SERIAL_FCR_RX_TRIGER_14 = 3 << 6, + SERIAL_FCR_DMA_SEL = 1 << 3, + SERIAL_FCR_TX_RESET = 1 << 2, + SERIAL_FCR_RX_RESET = 1 << 1, + SERIAL_FCR_FIFO_ENABLE = 1 << 0, + SERIAL_FCR_FIFO_RESET = SERIAL_FCR_TX_RESET | SERIAL_FCR_RX_RESET, + SERIAL_FCR_FIFO_DISABLE = 0 +}; + +enum SERIAL_IIR { + /** Mask for retrieving the interrupt that occured. See enum SERIAL_IID. */ + SERIAL_IIR_ID = 0xF, + + SERIAL_IIR_FIFO1 = 1 << 6, + SERIAL_IIR_FIFO2 = 1 << 7, +}; + +enum SERIAL_IID { + /** No interrupt. */ + SERIAL_IID_NONE = 1, + /** Highest priority: line error. OE, PE, etc. Serviced by reading LSR. */ + SERIAL_IID_ERROR = 6, + /** Second highest priority: data received or trigger reached. Serviced by reading data. */ + SERIAL_IID_DATA = 4, + /** Second highest priority: stale data in FIFO. Serviced by reading data. */ + SERIAL_IID_STALE = 0xC, + /** Third priority: transmitter buffer has room. Serviced by reading this reg/writing. */ + SERIAL_IID_THRE = 2, + /** Fourth priority: MSR change. Serviced by reading MSR. */ + SERIAL_IID_MODEM = 0, +}; + +enum SERIAL_LCR { + /** Word Length = 5 bits */ + SERIAL_LCR_WL_5 = 0, + SERIAL_LCR_WL_6 = 1, + SERIAL_LCR_WL_7 = 2, + SERIAL_LCR_WL_8 = 3, + /** STop Bit */ + SERIAL_LCR_STB = 1 << 2, + /** Parity ENable */ + SERIAL_LCR_PEN = 1 << 3, + /** Even Parity Select. */ + SERIAL_LCR_EPS = 1 << 4, + /** Stick Parity */ + SERIAL_LCR_SP = 1 << 5, + SERIAL_LCR_PAR_NONE = 0, + SERIAL_LCR_PAR_ODD = SERIAL_LCR_PEN, + SERIAL_LCR_PAR_EVEN = SERIAL_LCR_PEN | SERIAL_LCR_EPS, + SERIAL_LCR_PAR_MARK = SERIAL_LCR_PEN | SERIAL_LCR_SP, + SERIAL_LCR_PAR_SPACE = SERIAL_LCR_PEN | SERIAL_LCR_EPS | SERIAL_LCR_SP, + /** Set Break */ + SERIAL_LCR_SB = 1 << 6, + /** Divisor Latch Access Bit */ + SERIAL_LCR_DLAB = 1 << 7, +}; + +enum SERIAL_MCR { + SERIAL_MCR_DTR = 1 << 0, + SERIAL_MCR_RTS = 1 << 1, + SERIAL_MCR_OUT1 = 1 << 2, + SERIAL_MCR_OUT2 = 1 << 3, + SERIAL_MCR_LOOP = 1 << 4, +}; + +enum SERIAL_LSR { + /** Data Ready (i.e., data ready to be read) */ + SERIAL_LSR_DR = 1 << 0, + SERIAL_LSR_OE = 1 << 1, + SERIAL_LSR_PE = 1 << 2, + SERIAL_LSR_FE = 1 << 3, + SERIAL_LSR_BI = 1 << 4, + /** Transmitter Holding Register Empty (i.e. space to send data is available) */ + SERIAL_LSR_THRE = 1 << 5, + SERIAL_LSR_TEMT = 1 << 6, + SERIAL_LSR_RXERR = 1 << 7, +}; + +enum SERIAL_MSR { + SERIAL_MSR_DCD = 1 << 7, + SERIAL_MSR_RI = 1 << 6, + SERIAL_MSR_DSR = 1 << 5, + SERIAL_MSR_CTS = 1 << 4, + SERIAL_MSR_DDCD = 1 << 3, + SERIAL_MSR_TERI = 1 << 2, + SERIAL_MSR_DDSR = 1 << 1, + SERIAL_MSR_DCTS = 1 << 0 +}; + +struct serial_config +{ + uint16_t divisor; + uint8_t ier, lcr, mcr; +}; + +static unsigned serial_num_ports() +{ + uint16_t equipment = bda_get_equipment(); + uint8_t numports = (equipment & 0xE00) >> 9; + return numports; +} + +static uint16_t serial_get_iobase(unsigned port) +{ + uint16_t bdaport = bda_get_word((port - 1) * 2); + if (bdaport > 10) { + return bdaport; + } + + // Even if BIOS says no, hardcode basic COM1/COM2 at least. + switch (port) { + case 1: + return 0x3F8; + case 2: + return 0x2F8; + default: + return 0; + } +} + +static uint8_t serial_get_irq(unsigned port) +{ + switch (port) { + case 1: + case 3: + return 4; + case 2: + case 4: + return 3; + default: + return 0; + } +} + +static void serial_save_config(unsigned iobase, struct serial_config *config) +{ + config->ier = inp(iobase + SERIAL_IER); + config->lcr = inp(iobase + SERIAL_LCR); + config->mcr = inp(iobase + SERIAL_MCR); + // Set DLAB so that we can read divisor + outp(iobase + SERIAL_LCR, config->lcr | SERIAL_LCR_DLAB); + config->divisor = inpw(iobase + SERIAL_DIVISOR); + // Restore DLAB to whatever it was + outp(iobase + SERIAL_LCR, config->lcr); +} + +static void serial_restore_config(unsigned iobase, const struct serial_config *config) +{ + outp(iobase + SERIAL_LCR, config->lcr | SERIAL_LCR_DLAB); + outpw(iobase + SERIAL_DIVISOR, config->divisor); + outp(iobase + SERIAL_LCR, config->lcr); + outp(iobase + SERIAL_MCR, config->mcr); + outp(iobase + SERIAL_IER, config->ier); +} + +/// Configure line characteristics (speed, databytes, etc.) +/// Disables interrupts +static void serial_configure_line(unsigned iobase, uint16_t divisor, uint8_t lcr) +{ + outp(iobase + SERIAL_LCR, SERIAL_LCR_DLAB); // Enable DLAB and clear all other flags + outpw(iobase + SERIAL_DIVISOR, divisor); + outp(iobase + SERIAL_LCR, lcr); // This also disables DLAB +} + +/// Configures when exceptions are going to be raised +/// it also clears all pending interrupts +/// and enables/disables the OUT2 gator here, which I'm not sure if is conceptually clean +static void serial_configure_interrupts(unsigned iobase, uint8_t ier) +{ + uint8_t mcr = inp(iobase + SERIAL_MCR); + outp(iobase + SERIAL_IER, ier); + if (ier) { + mcr |= SERIAL_MCR_OUT2; // Actually enables IRQ line for this UART + } else { + mcr &= ~SERIAL_MCR_OUT2; + } + outp(iobase + SERIAL_MCR, mcr); +} + +static inline void serial_set_modem_control(unsigned iobase, uint8_t mcr) +{ + outp(iobase + SERIAL_MCR, mcr); +} + +static inline void serial_set_fifo_control(unsigned iobase, uint8_t fcr) +{ + outp(iobase + SERIAL_FCR, fcr); +} + +static inline uint8_t serial_read_pending_interrupt(unsigned iobase) +{ + return inp(iobase + SERIAL_IIR) & SERIAL_IIR_ID; +} + +static inline uint8_t serial_get_line_status(unsigned iobase) +{ + return inp(iobase + SERIAL_LSR); +} + +static inline uint8_t serial_get_modem_status(unsigned iobase) +{ + return inp(iobase + SERIAL_MSR); +} + +static inline bool serial_data_ready(unsigned iobase) +{ + return serial_get_line_status(iobase) & SERIAL_LSR_DR; +} + +static inline uint8_t serial_read_data(unsigned iobase) +{ + return inp(iobase + SERIAL_RX); +} + +static inline bool serial_tx_ready(unsigned iobase) +{ + return serial_get_line_status(iobase) & SERIAL_LSR_THRE; +} + +static inline void serial_send_data(unsigned iobase, uint8_t data) +{ + outp(iobase + SERIAL_TX, data); +} + +#endif // SERIAL_H diff --git a/sermouse.h b/sermouse.h new file mode 100644 index 0000000..fb2184d --- /dev/null +++ b/sermouse.h @@ -0,0 +1,50 @@ +/* + * VBMouse - Constants for serial mouse protocols + * 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 SERMOUSE_H +#define SERMOUSE_H + +enum SERMOUSE_DEVICE_ID +{ + SERMOUSE_DEVICE_ID_MS = 'M' +}; + +enum SERMOUSE_PROTOCOL_MS { + // 1st byte: + SERMOUSE_MS_BUTTON_LEFT = 1 << 5, + SERMOUSE_MS_BUTTON_RIGHT = 1 << 4, + + SERMOUSE_MS_X_LO_MASK = 0x3F, + SERMOUSE_MS_Y_LO_MASK = 0x3F, + + // 2nd byte: + SERMOUSE_MS_X_HI_MASK = 0x3, + SERMOUSE_MS_X_HI_SHIFT = 6, + + // 3rd byte: + SERMOUSE_MS_Y_HI_MASK = 0xC, + SERMOUSE_MS_Y_HI_SHIFT = 4, + + // 4th byte: + SERMOUSE_MS_BUTTON_CENTER = 1 << 5, + SERMOUSE_MS_Z_LO_MASK = 0xF, + SERMOUSE_MS_Z_LO_SHIFT = 0 +}; + +#endif // SERMOUSE_H @@ -30,10 +30,9 @@ #include "vboxshfl.h" #include "dostsr.h" #include "sftsr.h" +#include "unitbl.h" #include "unicode.h" -static nl_catd cat; - static char get_drive_letter(const char *path) { if (!path || path[0] == '\0') return '\0'; if (path[1] == '\0' || (path[1] == ':' && path[2] == '\0')) { @@ -169,13 +168,12 @@ static void close_openfiles(LPTSRDATA data, int drive) static int mount_shfl(LPTSRDATA data, int drive, const char *folder) { int32_t err; - SHFLSTRING_WITH_BUF(str, SHFL_MAX_LEN); + SHFLSTRING_WITH_BUF(utf8name, SHFL_MAX_LEN); SHFLROOT root = SHFL_ROOT_NIL; - unsigned flags = SHFL_MIQF_DRIVE_LETTER, version = 0; - shflstring_strcpy(&str.shflstr, folder); + utf8name.shflstr.u16Length = local_to_utf8(data, utf8name.buf, folder, utf8name.shflstr.u16Size); - err = vbox_shfl_map_folder(&data->vb, data->hgcm_client_id, &str.shflstr, &root); + err = vbox_shfl_map_folder(&data->vb, data->hgcm_client_id, &utf8name.shflstr, &root); if (err) { fprintf(stderr, _(3, 3, "Cannot mount shared folder '%s', err=%d\n"), folder, err); return -1; @@ -237,7 +235,7 @@ static bool is_mounted_drive(LPTSRDATA data, char drive_letter, int drive) return true; } -static int mount(LPTSRDATA data, char *folder, char drive_letter, MOUNTOPTS *opts) +static int mount(LPTSRDATA data, const char *folder, char drive_letter, MOUNTOPTS *opts) { int drive = drive_letter_to_index(drive_letter); DOSLOL __far *lol = dos_get_list_of_lists(); @@ -270,7 +268,6 @@ static int mount(LPTSRDATA data, char *folder, char drive_letter, MOUNTOPTS *opt // By setting the physical flag, we also let DOS know the drive is present cds->flags = DOS_CDS_FLAG_NETWORK | DOS_CDS_FLAG_PHYSICAL; - (void)utf8_to_local(data, folder, folder, NULL); printf(_(1, 3, "Shared folder '%s' mounted as drive %c:\n"), folder, drive_letter); return EXIT_SUCCESS; @@ -481,7 +478,8 @@ static void load_unicode_table(uint16_t far *unicode_table) char fullpath[_MAX_PATH]; char buffer[256]; FILE *f; - int i, ret; + const uint16_t *builtin_tbl; + int ret; // get current Code Page // @@ -502,6 +500,12 @@ static void load_unicode_table(uint16_t far *unicode_table) goto error; } + // Try one of the builtin tables first + if ((builtin_tbl = get_uni_tbl(r.x.bx))) { + _fmemcpy(unicode_table, builtin_tbl, 256); + return; + } + sprintf(filename, r.x.bx > 999 ? "c%duni.tbl" : "cp%duni.tbl", r.x.bx); // Search in the same directory as the executable first @@ -812,9 +816,8 @@ int main(int argc, const char *argv[]) { LPTSRDATA data = get_tsr_data(true); int err, argi = 1; - SHFLSTRING_WITH_BUF(utf8name, SHFL_MAX_LEN); - cat = kittenopen("vbsf"); + kittenopen(argv[0]); if (argi >= argc || stricmp(argv[argi], "install") == 0) { uint8_t hash_chars = DEF_HASH_CHARS; @@ -890,8 +893,7 @@ int main(int argc, const char *argv[]) err = parse_mountopts(&opts, &argi, argc, argv); if (err) return err; - local_to_utf8(data, utf8name.buf, folder, utf8name.shflstr.u16Size); - return mount(data, utf8name.buf, drive, &opts); + return mount(data, folder, drive, &opts); } else if (stricmp(argv[argi], "umount") == 0 || stricmp(argv[argi], "unmount") == 0) { char drive; if (!data) return driver_not_found(); diff --git a/nls/cp437uni.tbl b/unitbl/cp437uni.tbl Binary files differindex 76c962d..76c962d 100644 --- a/nls/cp437uni.tbl +++ b/unitbl/cp437uni.tbl diff --git a/nls/cp720uni.tbl b/unitbl/cp720uni.tbl Binary files differindex f8c868a..f8c868a 100644 --- a/nls/cp720uni.tbl +++ b/unitbl/cp720uni.tbl diff --git a/nls/cp737uni.tbl b/unitbl/cp737uni.tbl Binary files differindex abb1371..abb1371 100644 --- a/nls/cp737uni.tbl +++ b/unitbl/cp737uni.tbl diff --git a/nls/cp775uni.tbl b/unitbl/cp775uni.tbl Binary files differindex 6bc842d..6bc842d 100644 --- a/nls/cp775uni.tbl +++ b/unitbl/cp775uni.tbl diff --git a/nls/cp850uni.tbl b/unitbl/cp850uni.tbl Binary files differindex cefbfec..cefbfec 100644 --- a/nls/cp850uni.tbl +++ b/unitbl/cp850uni.tbl diff --git a/nls/cp852uni.tbl b/unitbl/cp852uni.tbl Binary files differindex 8809854..8809854 100644 --- a/nls/cp852uni.tbl +++ b/unitbl/cp852uni.tbl diff --git a/nls/cp855uni.tbl b/unitbl/cp855uni.tbl Binary files differindex e5bdd65..e5bdd65 100644 --- a/nls/cp855uni.tbl +++ b/unitbl/cp855uni.tbl diff --git a/nls/cp857uni.tbl b/unitbl/cp857uni.tbl Binary files differindex a5d83b4..a5d83b4 100644 --- a/nls/cp857uni.tbl +++ b/unitbl/cp857uni.tbl diff --git a/nls/cp858uni.tbl b/unitbl/cp858uni.tbl Binary files differindex 9f4dd59..9f4dd59 100644 --- a/nls/cp858uni.tbl +++ b/unitbl/cp858uni.tbl diff --git a/nls/cp861uni.tbl b/unitbl/cp861uni.tbl Binary files differindex b76628e..b76628e 100644 --- a/nls/cp861uni.tbl +++ b/unitbl/cp861uni.tbl diff --git a/nls/cp862uni.tbl b/unitbl/cp862uni.tbl Binary files differindex 03b9559..03b9559 100644 --- a/nls/cp862uni.tbl +++ b/unitbl/cp862uni.tbl diff --git a/nls/cp863uni.tbl b/unitbl/cp863uni.tbl Binary files differindex 7f424b0..7f424b0 100644 --- a/nls/cp863uni.tbl +++ b/unitbl/cp863uni.tbl diff --git a/nls/cp864uni.tbl b/unitbl/cp864uni.tbl Binary files differindex 1a03811..1a03811 100644 --- a/nls/cp864uni.tbl +++ b/unitbl/cp864uni.tbl diff --git a/nls/cp865uni.tbl b/unitbl/cp865uni.tbl Binary files differindex be5dd95..be5dd95 100644 --- a/nls/cp865uni.tbl +++ b/unitbl/cp865uni.tbl diff --git a/nls/cp866uni.tbl b/unitbl/cp866uni.tbl Binary files differindex cdc959d..cdc959d 100644 --- a/nls/cp866uni.tbl +++ b/unitbl/cp866uni.tbl diff --git a/nls/cp869uni.tbl b/unitbl/cp869uni.tbl Binary files differindex af30476..af30476 100644 --- a/nls/cp869uni.tbl +++ b/unitbl/cp869uni.tbl diff --git a/nls/cp874uni.tbl b/unitbl/cp874uni.tbl Binary files differindex 455ee0b..455ee0b 100644 --- a/nls/cp874uni.tbl +++ b/unitbl/cp874uni.tbl diff --git a/nls/license.txt b/unitbl/license.txt index 85d0d58..85d0d58 100644 --- a/nls/license.txt +++ b/unitbl/license.txt diff --git a/unitbl2c.c b/unitbl2c.c new file mode 100644 index 0000000..f2e76cc --- /dev/null +++ b/unitbl2c.c @@ -0,0 +1,206 @@ +/* + * VBSF - generate C file with codepage -> unicode tables based on uni*.tbl files + * 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 <stdint.h> +#include <libgen.h> +#include <ctype.h> +#include <string.h> + +/** size of codepage -> unicode table */ +#define TBL_SIZE 128 /* in 16bit words */ +/** maximum number of codepages in the same C file */ +#define MAX_CPS 32 + +static FILE *fout; +static unsigned all_cps[MAX_CPS] = {0}; +static unsigned num_cps = 0; + +static void print_table(const uint16_t *table, unsigned cp) +{ + int i; + int col = 0; + + fprintf(fout, "static const uint16_t cp%utbl[%u] = {\n ", cp, TBL_SIZE); + + for (i = 0; i < TBL_SIZE; i++) + { + if (col) + { + fprintf(fout, ", "); + } + if (col >= 8) + { + fprintf(fout, "\n "); + col = 0; + } + fprintf(fout, "0x%04X", table[i]); + col++; + } + + fprintf(fout, "\n};\n\n"); +} + +static int process_table(FILE *fp, const char *base, unsigned cp) +{ + char buffer[TBL_SIZE*2]; + int err; + + // Read "Description of table" (terminated by \r\n) + if (!fgets(buffer, sizeof(buffer), fp)) { + return -1; + } + + fprintf(fout, "// %s", buffer); + + // Read "Table format designator" (currently only format 1 is supported) + err = fread(buffer, 1, 1, fp); + if (err != 1 || buffer[0] != 1) { + return -1; + } + + // Now read the actual table (256 bytes in format 1) + if ((err = fread(buffer, 1, sizeof(buffer), fp)) != sizeof(buffer)) { + return -1; + } + + print_table((const uint16_t *)buffer, cp); + + all_cps[num_cps++] = cp; + + return 0; +} + +static int process_file(char *filename) +{ + FILE *fp = fopen(filename, "rb"); + char *base = basename(filename); + unsigned cp = 0; + + if (!fp) { + fprintf(stderr, "Cannot open file '%s'\n", filename); + return -1; + } + + if (strnicmp(&base[0], "CP", 2) == 0 + && isdigit(base[2]) && isdigit(base[3]) && isdigit(base[4]) + && strnicmp(&base[5], "UNI.TBL", 7) == 0) { + cp = atoi(&base[2]); + } else if (strnicmp(&base[0], "C", 2) == 0 + && isdigit(base[1]) && isdigit(base[2]) && isdigit(base[3]) && isdigit(base[4]) + && strnicmp(&base[5], "UNI.TBL", 7) == 0) { + cp = atoi(&base[1]); + } + + if (!cp) { + fprintf(stderr, "Cannot guess codepage number from filename '%s'\n", base); + fclose(fp); + return -1; + } + + if (process_table(fp, base, cp) != 0) { + fprintf(stderr, "Cannot read table format from '%s'\n", base); + fclose(fp); + return -1; + } + + fclose(fp); + return 0; +} + +#ifdef __UNIX__ +// no need to process wildcards on UNIX +static int process_arg(char *arg) +{ + return process_file(arg); +} +#else +#include <io.h> + +static int process_arg(char *arg) +{ + struct _finddata_t fileinfo; + intptr_t fh = _findfirst(arg, &fileinfo); + + if (!fh) { + fprintf(stderr, "Cannot find '%s'\n", arg); + return -1; + } + + do { + // Name of file in fileinfo will be without path + // If so copy the path from the arg + char namebuf[_MAX_PATH]; + char *s; + + if ((s = strrchr(arg, '\\')) || (s = strrchr(arg, '/')) || (s = strrchr(arg, ':'))) { + memcpy(namebuf, arg, s-arg+1); + strcpy(namebuf + (s-arg) +1, fileinfo.name); + } else { + strcpy(namebuf, fileinfo.name); + } + + if (process_file(namebuf) != 0) { + return -1; + } + } while (_findnext(fh, &fileinfo) == 0); + + return 0; +} +#endif + +int main(int argc, char **argv) +{ + int i; + + if (argc < 3) + { + fprintf(stderr, + "Usage: bin2c <output c file> <unitbl/*.tbl>\n"); + return 1; + } + + fout = fopen(argv[1], "wt"); + fprintf(fout, "#include <stdint.h>\n"); + + for (i = 2; i < argc; i++) { + if (process_arg(argv[i])) { + return EXIT_FAILURE; + } + } + + fprintf(fout, "static const uint16_t * get_uni_tbl(unsigned cp) {\n"); + fprintf(fout, " switch (cp) {\n"); + for (i = 0; i < num_cps; i++) { + unsigned cp = all_cps[i]; + fprintf(fout, " case %u:\n", cp); + fprintf(fout, " return cp%utbl;\n", cp); + } + fprintf(fout, " default:\n"); + fprintf(fout, " return 0;\n"); + fprintf(fout, " }\n"); + fprintf(fout, "}\n\n"); + + fclose(fout); + + printf("Processed %u tables\n", num_cps); + + return EXIT_SUCCESS; +} @@ -92,4 +92,11 @@ static int scalei_rem(int x, int srcmax, int dstmax, short *rem); __value [ax] \ __modify [ax cx dx si] +/** Sign extend x from b bits to 16. */ +static inline int16_t sign_extend(int16_t x, int b) +{ + int m = 16 - b; + return (x << m) >> m; +} + #endif @@ -2,6 +2,6 @@ #define VERSION_H #define VERSION_MAJOR 0 -#define VERSION_MINOR 0x68 +#define VERSION_MINOR 0x71 #endif // VERSION_H |
