aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md5
-rw-r--r--bda.h69
-rw-r--r--dlog.h58
-rw-r--r--doc/absapi.txt22
-rw-r--r--doc/int33.lst732
-rw-r--r--doc/wheelapi.txt109
-rw-r--r--doc/whlapi2.txt69
-rw-r--r--int10vga.h29
-rw-r--r--int15ps2.h90
-rw-r--r--int16kbd.h4
-rw-r--r--int33.h79
-rw-r--r--kitten.c786
-rw-r--r--kitten.h42
-rw-r--r--kittenc.c548
-rw-r--r--kittenc.h44
-rw-r--r--makefile51
-rw-r--r--mousetsr.c625
-rw-r--r--mousetsr.h90
-rw-r--r--mousew16.c165
-rw-r--r--mousew16.lnk6
-rw-r--r--mousmain.c527
-rw-r--r--moustest.c814
-rw-r--r--nls/.gitattributes2
-rw-r--r--nls/vbmouse.en26
-rw-r--r--nls/vbmouse.es26
-rw-r--r--nls/vbmouse.fr61
-rw-r--r--nls/vbmouse.tr61
-rw-r--r--nls/vbsf.fr60
-rw-r--r--nls/vbsf.tr60
-rw-r--r--pic.h59
-rw-r--r--serial.h291
-rw-r--r--sermouse.h50
-rw-r--r--sfmain.c28
-rw-r--r--unitbl/cp437uni.tbl (renamed from nls/cp437uni.tbl)bin282 -> 282 bytes
-rw-r--r--unitbl/cp720uni.tbl (renamed from nls/cp720uni.tbl)bin276 -> 276 bytes
-rw-r--r--unitbl/cp737uni.tbl (renamed from nls/cp737uni.tbl)bin274 -> 274 bytes
-rw-r--r--unitbl/cp775uni.tbl (renamed from nls/cp775uni.tbl)bin276 -> 276 bytes
-rw-r--r--unitbl/cp850uni.tbl (renamed from nls/cp850uni.tbl)bin281 -> 281 bytes
-rw-r--r--unitbl/cp852uni.tbl (renamed from nls/cp852uni.tbl)bin283 -> 283 bytes
-rw-r--r--unitbl/cp855uni.tbl (renamed from nls/cp855uni.tbl)bin277 -> 277 bytes
-rw-r--r--unitbl/cp857uni.tbl (renamed from nls/cp857uni.tbl)bin276 -> 276 bytes
-rw-r--r--unitbl/cp858uni.tbl (renamed from nls/cp858uni.tbl)bin281 -> 281 bytes
-rw-r--r--unitbl/cp861uni.tbl (renamed from nls/cp861uni.tbl)bin278 -> 278 bytes
-rw-r--r--unitbl/cp862uni.tbl (renamed from nls/cp862uni.tbl)bin275 -> 275 bytes
-rw-r--r--unitbl/cp863uni.tbl (renamed from nls/cp863uni.tbl)bin276 -> 276 bytes
-rw-r--r--unitbl/cp864uni.tbl (renamed from nls/cp864uni.tbl)bin275 -> 275 bytes
-rw-r--r--unitbl/cp865uni.tbl (renamed from nls/cp865uni.tbl)bin275 -> 275 bytes
-rw-r--r--unitbl/cp866uni.tbl (renamed from nls/cp866uni.tbl)bin276 -> 276 bytes
-rw-r--r--unitbl/cp869uni.tbl (renamed from nls/cp869uni.tbl)bin275 -> 275 bytes
-rw-r--r--unitbl/cp874uni.tbl (renamed from nls/cp874uni.tbl)bin273 -> 273 bytes
-rw-r--r--unitbl/license.txt (renamed from nls/license.txt)0
-rw-r--r--unitbl2c.c206
-rw-r--r--utils.h7
-rw-r--r--version.h2
54 files changed, 4810 insertions, 1093 deletions
diff --git a/README.md b/README.md
index e51ee47..97001c4 100644
--- a/README.md
+++ b/README.md
@@ -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.
diff --git a/bda.h b/bda.h
new file mode 100644
index 0000000..c479182
--- /dev/null
+++ b/bda.h
@@ -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
diff --git a/dlog.h b/dlog.h
index ebeb01c..19337cd 100644
--- a/dlog.h
+++ b/dlog.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.
diff --git a/int10vga.h b/int10vga.h
index c27aec1..552b153 100644
--- a/int10vga.h
+++ b/int10vga.h
@@ -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,
diff --git a/int15ps2.h b/int15ps2.h
index dfc5b3a..04d5a8d 100644
--- a/int15ps2.h
+++ b/int15ps2.h
@@ -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 */
diff --git a/int16kbd.h b/int16kbd.h
index 45db8ef..3b2c860 100644
--- a/int16kbd.h
+++ b/int16kbd.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]
diff --git a/int33.h b/int33.h
index 03c61a0..df870be 100644
--- a/int33.h
+++ b/int33.h
@@ -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 */
diff --git a/kitten.c b/kitten.c
index f554047..421ed81 100644
--- a/kitten.c
+++ b/kitten.c
@@ -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 */
diff --git a/kitten.h b/kitten.h
index 0761695..b38b688 100644
--- a/kitten.h
+++ b/kitten.h
@@ -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 */
diff --git a/makefile b/makefile
index 907a8e5..efa98a3 100644
--- a/makefile
+++ b/makefile
@@ -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/*
diff --git a/mousetsr.c b/mousetsr.c
index 8c42057..0965b6b 100644
--- a/mousetsr.c
+++ b/mousetsr.c
@@ -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
diff --git a/mousetsr.h b/mousetsr.h
index 43fccc1..704f670 100644
--- a/mousetsr.h
+++ b/mousetsr.h
@@ -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);
diff --git a/mousew16.c b/mousew16.c
index fd25609..13cf310 100644
--- a/mousew16.c
+++ b/mousew16.c
@@ -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
diff --git a/mousmain.c b/mousmain.c
index f457adf..24801b7 100644
--- a/mousmain.c
+++ b/mousmain.c
@@ -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 d‚faut).
+0.4: low dans m‚moire conventionnelle (sinon UMB).
+0.5: ps2, com1, com2, ... sur quels ports rechercher une souris.
+0.6: uninstall D‚sinstaller le pilote de la m‚moire.
+0.7: rescan <ps2|com1|..> Chercher la souris … nouveau sur les ports indiqu‚s.
+0.8: wheel <ON|OFF> Activer/d‚sactiver le support de l'API roulette.
+0.9: wheelkey <TOUCHE|OFF> muler un appui lors du d‚filement 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/d‚sactiver l'int‚gration VirtualBox.
+0.14: hostcur <ON|OFF> Activer/d‚sactiver rendu du curseur par l'h“te.
+0.15: reset R‚initialiser le pilote de la souris.
+1.0:Souris PS/2 trouv‚e avec %u boutons, %u roulettes\n
+1.1:R‚glage de la prise en charge de la roulette sur %s\n
+1.2:activ‚
+1.3:d‚sactiv‚
+1.4:G‚n‚rer Curseur haut / Curseur bas lors des mouvements de la roulette\n
+1.5:G‚n‚rer Page haute / Page basse lors des mouvements de la roulette\n
+1.6:D‚sactivation de la g‚n‚ration d'appuis de touches avec le roulette\n
+1.7:Int‚gration VirtualBox activ‚e\n
+1.8:Int‚gration VirtualBox d‚sactiv‚e\n
+1.9:L'int‚gration VirtualBox d‚j… d‚sactiv‚e ou bien non disponible\n
+1.10:R‚glage du curseur de l'h“te sur %s\n
+1.11:Protocole VMware version %ld trouv‚\n
+1.12:Int‚gration VMware activ‚e\n
+1.13:Int‚gration VMware d‚sactiv‚e\n
+1.14:L'int‚gration VMware d‚j… d‚sactiv‚e ou bien non disponible\n
+1.15:Ni l'int‚gration VirtualBox ni celle de VMware ne sont disponibles\n
+1.16:L'int‚gration VirtualBox n'est pas disponible\n
+1.17:Pilote install‚\n
+1.18:Pilote d‚sinstall‚\n
+1.19:Pilote de souris r‚initialis‚\n
+1.20:\nVBMouse %x.%x (similaire … MSMOUSE %x.%x)\n
+1.21:VBMouse d‚j… install‚\n
+1.22:Souris s‚rie trouv‚e sur COM%u avec %u boutons, %u roulettes\n
+3.0:Souris PS/2 … roulette introuvable\n
+3.1:Roulette non d‚tect‚e ou bien prise en charge non activ‚e\n
+3.2:Touche inconnue '%s'\n
+3.3:Impossible de trouver le p‚riph‚rique 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 d‚tecter 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 fa‡on s–re\n
+3.10:INT2F intercept‚ par quelqu'un d'autre, impossible de l'enlever de fa‡on s–re\n
+3.11:Les donn‚es du pilote sont introuvables (pilote non install‚ ?)\n
+3.12:Argument invalide '%s'\n
+3.13:Argument n‚cessaire pour '%s'\n
+3.14:Aucune souris trouv‚e\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:Kullanm:
+0.1: VBMOUSE <EYLEM> <ARGšMANLAR..>
+0.2:Desteklenen eylem ve se‡enekler:
+0.3: install Src yazlmn kur (varsaylan).
+0.4: low Geleneksel belle§e ykle (yoksa UMB).
+0.5: ps2, com1, com2, ... Specify on which ports to search a mouse.
+0.6: uninstall Src yazlmn bellekten kaldr.
+0.7: rescan <ps2|com1|..> Rescan for mouse in specified ports.
+0.8: wheel <ON|OFF> Tekerlek API deste§ini etkinleŸtir/devre dŸ brak.
+0.9: wheelkey <TUž|OFF> Tekerle§i kaydrmada belli bir tuŸ basmn taklit
+0.10: et. Desteklenen tuŸlar: updn, pageupdn
+0.11: hwheelkey <KEY|OFF> Likewise for horizontal wheel scroll.
+0.12: Supported keys: lr.
+0.13: integ <ON|OFF> VirtualBox birleŸimini etkinleŸtir/deve dŸ brak.
+0.14: hostcur <ON|OFF> Fare imleci g”rntlenmesini etkinleŸtir/devre dŸ.
+0.15: reset Fare srcsn sfrla.
+1.0:Found PS/2 mouse with %u buttons, %u wheels\n
+1.1:Tekerlek deste§i %s olarak ayarlanyor\n
+1.2:etkinleŸtirilmiŸ
+1.3:devre dŸ
+1.4:Tekerlek hareketinde Yukar ˜mle‡ / AŸa§ ˜mle‡ tuŸ basmlarn oluŸtur\n
+1.5:Tekerlek hareketinde Yukar Sayfa / AŸa§ Sayfa tuŸ basmlarn oluŸtur\n
+1.6:Tekerlek ile tuŸ basm oluŸturulmas devre dŸ braklyor\n
+1.7:VirtualBox birleŸimi etkinleŸtirildi\n
+1.8:VirtualBox birleŸimi devre dŸ brakld\n
+1.9:VirtualBox birleŸimi zaten devre dŸ veya mevcut de§il\n
+1.10:Ev sahibi bilgisayardaki imle‡ %s olarak ayarlanyor\n
+1.11:VMware protokol srm %ld bulundu\n
+1.12:VMware birleŸimi etkinleŸtirildi\n
+1.13:VMware birleŸimi devre dŸ brakld\n
+1.14:VMware birleŸimi zaten devre dŸ veya mevcut de§il\n
+1.15:Ne VirtualBox ne de VMware birleŸimi mevcut de§il\n
+1.16:VirtualBox birleŸimi mevcut de§il\n
+1.17:Src kuruldu\n
+1.18:Src kaldrld\n
+1.19:Fare srcs sfrland\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 etkinleŸtirilmemiŸ\n
+3.2:Bilinmeyen tuŸ '%s'\n
+3.3:VirtualBox PCI cihaz bulunamaz, hata=%d\n
+3.4:VirtualBox iletiŸimi i‡in kullanlan tampon kilitlenemez, hata=%d\n
+3.5:VirtualBox iletiŸimi ‡alŸmyor, 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 baŸlatlamaz, hata=%d\n
+3.9:INT33 baŸkas tarafndan ele ge‡irilmiŸ, gvenli bir Ÿekilde kaldrlamaz\n
+3.10:INT2F baŸkas tarafndan ele ge‡irilmiŸ, gvenli bir Ÿekilde kaldrlamaz\n
+3.11:Src yazlm verileri bulunamad (src yazlm kurulu de§il mi?)\n
+3.12:Ge‡ersiz argman '%s'\n
+3.13:'%s' i‡in argman 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 d‚faut).
+0.4: low dans m‚moire conventionnelle (sinon UMB).
+0.5: uninstall D‚sinstaller le pilote de la m‚moire.
+0.6: list Lister les r‚pertoires partag‚s disponibles.
+0.7: mount <DOSSIER> <X:> ... Monter un dossier partag‚ sur le lecteur X:.
+0.8: /hash <n> Nombre de caractŠres de hachage suivant le '~'
+0.9: pour les noms de fichiers courts DOS g‚n‚r‚s.
+0.10: (entre %d et %d; par d‚faut %d; 0 d‚sactiver)\n
+0.11: /host Utiliser les noms courts depuis les h“tes Win.
+0.12: /upper Exiger des noms de fichiers h“tes en majuscules.
+0.13: remount <X:> ... Modifier options de montage pour lecteur X:.
+0.14: umount <X:> D‚monter le dossier partag‚ du lecteur X:.
+0.15: rescan D‚monter tout et recr‚er des montage automatiques.
+1.0:Lecteurs mont‚s :\n
+1.1: %s sur %c: %s\n
+1.2:Dossiers partag‚s disponibles :\n
+1.3:Dossier partag‚ '%s' mont‚ en tant que lecteur %c:\n
+1.4:Lecteur %c: d‚mont‚\n
+1.5:Utilisation du fuseau horaire depuis la variable TZ (%s)\n
+1.6:Connect‚ au service de dossiers partag‚s de VirtualBox\n
+1.7:Pilote install‚\n
+1.8:Pilote d‚sinstall‚\n
+1.9:\nVBSharedFolders %x.%x\n
+1.10:VBSF d‚j… 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 d‚faut\n
+3.0:Erreur lors de Query Map Name, (requˆte de nom de mappage) err=%ld\n
+3.1:Erreur lors de Query Mappings (mappage des requˆtes), err=%ld\n
+3.2:Erreur lors de Close File (fermeture de fichier), err=%ld\n
+3.3:Impossible de monter le r‚pertoire partag‚ '%s', err=%d\n
+3.4:Erreur lors de Query Map Info pour le lecteur mont‚, err=%ld\n
+3.5:Impossible de d‚monter le r‚pertoire partag‚, err=%d\n
+3.6:Lecteur invalide %c:\n
+3.7:Le lecteur %c: est aprŠs LASTDRIVE (dernier lecteur)\n
+3.8:Lecteur %c d‚j… mont‚\n
+3.9:Lecteur %c : existe d‚j…\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 p‚riph‚rique 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 partag‚s, err=%ld\n
+3.18:Impossible de configurer UTF-8 sur le service de dossiers partag‚s, err=%ld\n
+3.19:INT2F intercept‚ par quelqu'un d'autre, impossible de l'enlever de fa‡on s–re\n
+3.20:Les donn‚es du pilote sont introuvables (pilote non install‚ ?)\n
+3.21:Argument invalide '%s'\n
+3.22:Argument n‚cessaire 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:Kullanm:
+0.1: VBSF <EYLEM> <ARGšMANLAR..> [<SE€ENEKLER..>]
+0.2:Desteklenen eylem ve se‡enekler:
+0.3: install Srcy kur (varsaylan).
+0.4: low Geleneksel belle§e ykle (yoksa UMB).
+0.5: uninstall Src yazlmn bellekten kaldr.
+0.6: list Mevcut paylaŸlmŸ dizinleri listele.
+0.7: mount <FOLD> <X:> ... X: srcsne paylaŸlmŸ bir dosya ba§la.
+0.8: /hash <n> '~' iŸaretini takip eden karma karakter says,
+0.9: oluŸturulmuŸ DOS ksa dosya isimleri i‡in.
+0.10: (%d ve %d aras; varsaylan %d; 0 devre dŸ)\n
+0.11: /host Windows bilgisayarndan ksa doysa adlar kullan.
+0.12: /upper Byk harfli dosya adlar talep et.
+0.13: remount <X:> ... Ba§l src X: i‡in ba§lama se‡eneklerini de§iŸtir.
+0.14: umount <X:> X: srcsnden paylaŸlmŸ dizin ba§n kaldr.
+0.15: rescan Tm ba§lar kaldr ve tekrar otomatik oluŸtur.
+1.0:Ba§l srcler:\n
+1.1: %s, %c zerinde: %s\n
+1.2:Mevcut paylaŸlmŸ dizinler:\n
+1.3:'%s' paylaŸlmŸ dizini %c: srcs olarak ba§land\n
+1.4:%c: srcsnn ba§ kaldrld\n
+1.5:Saat dilimi TZ de§iŸkeninden kullanlyor (%s)\n
+1.6:VirtualBox paylaŸlmŸ dizin hizmetine ba§lanld\n
+1.7:Src yazlm kuruldu\n
+1.8:Src yazlm kaldrld\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 yklenemedi: %s
+2.3:˜kaz: ge‡ersiz dosya bi‡imi: %s
+2.4:. Varsaylan olarak cp437 kullanlyor\n
+3.0:Query Map Name esnasnda hata, hata=%ld\n
+3.1:Query Mappings esnasnda hata, hata=%ld\n
+3.2:Dosya kapatmas esnasnda hata, hata=%ld\n
+3.3:PaylaŸlmŸ dizin '%s' ba§lanamaz, hata=%d\n
+3.4:Ba§l src i‡in Query Map Info esnasnda hata, hata=%ld\n
+3.5:PaylaŸlmŸ dizinin ba§ kaldrlamaz, hata=%d\n
+3.6:Ge‡ersiz src %c:\n
+3.7:Src %c: LASTDRIVE (son srcden) sonra\n
+3.8:Src %c zaten ba§l\n
+3.9:Src %c: zaten mevcut\n
+3.11:Src %c ba§l de§il\n
+3.12:Error en Query Map Info
+3.13:NLS tablolar alnamaz
+3.14:VirtualBox PCI cihaz bulunamad, hata=%ld\n
+3.15:VirtualBox iletiŸimi i‡in kullanlan tampon kilitlenemez, hata=%ld\n
+3.16:VirtualBox iletiŸimi ‡alŸmyor, hata=%ld\n
+3.17:PaylaŸlmŸ dizin hizmetine ba§lanlamaz, hata=%ld\n
+3.18:UTF-8, paylaŸlmŸ dizin hizmetinde yaplandrlamaz, hata=%ld\n
+3.19:INT2F baŸkas tarafndan ele ge‡irilmiŸ, gvenli bir Ÿekilde kaldrlamaz\n
+3.20:Src yazlm verileri bulunamad (src yazlm kurulu de§il mi?)\n
+3.21:Ge‡ersiz argman '%s'\n
+3.22:'%s' i‡in argman gerekli\n
diff --git a/pic.h b/pic.h
new file mode 100644
index 0000000..0d87369
--- /dev/null
+++ b/pic.h
@@ -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
diff --git a/sfmain.c b/sfmain.c
index 59acbca..4f102bb 100644
--- a/sfmain.c
+++ b/sfmain.c
@@ -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
index 76c962d..76c962d 100644
--- a/nls/cp437uni.tbl
+++ b/unitbl/cp437uni.tbl
Binary files differ
diff --git a/nls/cp720uni.tbl b/unitbl/cp720uni.tbl
index f8c868a..f8c868a 100644
--- a/nls/cp720uni.tbl
+++ b/unitbl/cp720uni.tbl
Binary files differ
diff --git a/nls/cp737uni.tbl b/unitbl/cp737uni.tbl
index abb1371..abb1371 100644
--- a/nls/cp737uni.tbl
+++ b/unitbl/cp737uni.tbl
Binary files differ
diff --git a/nls/cp775uni.tbl b/unitbl/cp775uni.tbl
index 6bc842d..6bc842d 100644
--- a/nls/cp775uni.tbl
+++ b/unitbl/cp775uni.tbl
Binary files differ
diff --git a/nls/cp850uni.tbl b/unitbl/cp850uni.tbl
index cefbfec..cefbfec 100644
--- a/nls/cp850uni.tbl
+++ b/unitbl/cp850uni.tbl
Binary files differ
diff --git a/nls/cp852uni.tbl b/unitbl/cp852uni.tbl
index 8809854..8809854 100644
--- a/nls/cp852uni.tbl
+++ b/unitbl/cp852uni.tbl
Binary files differ
diff --git a/nls/cp855uni.tbl b/unitbl/cp855uni.tbl
index e5bdd65..e5bdd65 100644
--- a/nls/cp855uni.tbl
+++ b/unitbl/cp855uni.tbl
Binary files differ
diff --git a/nls/cp857uni.tbl b/unitbl/cp857uni.tbl
index a5d83b4..a5d83b4 100644
--- a/nls/cp857uni.tbl
+++ b/unitbl/cp857uni.tbl
Binary files differ
diff --git a/nls/cp858uni.tbl b/unitbl/cp858uni.tbl
index 9f4dd59..9f4dd59 100644
--- a/nls/cp858uni.tbl
+++ b/unitbl/cp858uni.tbl
Binary files differ
diff --git a/nls/cp861uni.tbl b/unitbl/cp861uni.tbl
index b76628e..b76628e 100644
--- a/nls/cp861uni.tbl
+++ b/unitbl/cp861uni.tbl
Binary files differ
diff --git a/nls/cp862uni.tbl b/unitbl/cp862uni.tbl
index 03b9559..03b9559 100644
--- a/nls/cp862uni.tbl
+++ b/unitbl/cp862uni.tbl
Binary files differ
diff --git a/nls/cp863uni.tbl b/unitbl/cp863uni.tbl
index 7f424b0..7f424b0 100644
--- a/nls/cp863uni.tbl
+++ b/unitbl/cp863uni.tbl
Binary files differ
diff --git a/nls/cp864uni.tbl b/unitbl/cp864uni.tbl
index 1a03811..1a03811 100644
--- a/nls/cp864uni.tbl
+++ b/unitbl/cp864uni.tbl
Binary files differ
diff --git a/nls/cp865uni.tbl b/unitbl/cp865uni.tbl
index be5dd95..be5dd95 100644
--- a/nls/cp865uni.tbl
+++ b/unitbl/cp865uni.tbl
Binary files differ
diff --git a/nls/cp866uni.tbl b/unitbl/cp866uni.tbl
index cdc959d..cdc959d 100644
--- a/nls/cp866uni.tbl
+++ b/unitbl/cp866uni.tbl
Binary files differ
diff --git a/nls/cp869uni.tbl b/unitbl/cp869uni.tbl
index af30476..af30476 100644
--- a/nls/cp869uni.tbl
+++ b/unitbl/cp869uni.tbl
Binary files differ
diff --git a/nls/cp874uni.tbl b/unitbl/cp874uni.tbl
index 455ee0b..455ee0b 100644
--- a/nls/cp874uni.tbl
+++ b/unitbl/cp874uni.tbl
Binary files differ
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;
+}
diff --git a/utils.h b/utils.h
index 715294e..9b9db2a 100644
--- a/utils.h
+++ b/utils.h
@@ -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
diff --git a/version.h b/version.h
index abbbca7..1df35fd 100644
--- a/version.h
+++ b/version.h
@@ -2,6 +2,6 @@
#define VERSION_H
#define VERSION_MAJOR 0
-#define VERSION_MINOR 0x68
+#define VERSION_MINOR 0x71
#endif // VERSION_H