From 4a8fe8127c57b0953e2ba2ad4da5e45a20300645 Mon Sep 17 00:00:00 2001 From: Javier Date: Sun, 19 May 2024 21:03:50 +0200 Subject: add initial support for 2nd wheel & 4-5 mouse buttons --- doc/absapi.txt | 22 ++ doc/int33.lst | 732 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ doc/wheelapi.txt | 109 ++++++++ doc/wheelapi2.txt | 70 ++++++ int15ps2.h | 54 +++- int33.h | 16 +- mousetsr.c | 293 +++++++++++++++------- mousetsr.h | 36 ++- mousmain.c | 21 +- utils.h | 7 + version.h | 2 +- 11 files changed, 1230 insertions(+), 132 deletions(-) create mode 100644 doc/absapi.txt create mode 100644 doc/int33.lst create mode 100644 doc/wheelapi.txt create mode 100644 doc/wheelapi2.txt 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/wheelapi2.txt b/doc/wheelapi2.txt new file mode 100644 index 0000000..91acd32 --- /dev/null +++ b/doc/wheelapi2.txt @@ -0,0 +1,70 @@ +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 enable/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 +Note: calling this function enables support for BOTH wheels + (until the next int33/0 reset call). + To receive 2nd wheel interrupts,, bit 9 must be set when registering + user interrupt routines (see INT 33/000C). + +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. + SI = 16-bit signed counter of 2nd wheel movement since last call + positive value means leftward 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 +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 + +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. + +Remark: 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. + Maybe this isn't so bad. diff --git a/int15ps2.h b/int15ps2.h index dfc5b3a..de8a260 100644 --- a/int15ps2.h +++ b/int15ps2.h @@ -22,6 +22,7 @@ #include #include +#include "utils.h" /** Standard PS/2 mouse IRQ. At least on VirtualBox. */ #define PS2_MOUSE_IRQ 12 @@ -49,19 +50,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. */ @@ -200,7 +209,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 +217,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 +249,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,7 +265,6 @@ 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; } diff --git a/int33.h b/int33.h index 03c61a0..8783d20 100644 --- a/int33.h +++ b/int33.h @@ -150,7 +150,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 +168,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 +186,14 @@ 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_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. */ diff --git a/mousetsr.c b/mousetsr.c index 8c42057..dad4229 100644 --- a/mousetsr.c +++ b/mousetsr.c @@ -537,6 +537,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,6 +549,21 @@ static void refresh_video_info(void) } } +/** Obtains INT33_EVENT_MASK bitmask corresponding to btn. */ +static uint16_t button_event_mask(int btn, bool released) +{ + 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(void (__far *handler)(), uint16_t events, uint16_t buttons, int16_t x, int16_t y, @@ -574,16 +591,17 @@ static void call_event_handler(void (__far *handler)(), uint16_t events, * @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,9 +665,18 @@ 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)) { - // Emulate keystrokes on wheel movement + if (data.num_wheels && z) { + if (data.usewheelapi) { + 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[wheeln].delta += z; + data.wheel[wheeln].last.x = data.pos.x; + data.wheel[wheeln].last.y = data.pos.y; + } else if (wheeln == 0 && (data.wheel_up_key || data.wheel_down_key)) { + // Emulate keystrokes on (vertical) wheel movement if (z < 0 && data.wheel_up_key) { for (; z < 0; z++) { int16_store_keystroke(data.wheel_up_key); @@ -659,31 +686,23 @@ static void handle_mouse_event(uint16_t buttons, bool absolute, int x, int y, in int16_store_keystroke(data.wheel_down_key); } } - } 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; } } #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 +713,78 @@ 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); + + call_event_handler(data.event_handler, events, + buttons, x, y, data.delta.x, data.delta.y); } static void handle_ps2_packet(void) { - unsigned status; - int x, y, z = 0; - bool abs = false; + unsigned status = data.ps2_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.ps2_packet[1], y = data.ps2_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.ps2_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.ps2_packet[3] & PS2M_IMEX_VERTICAL_SCROLL) { + // Vertical scrolling, with 6 bits of precision. + z = sign_extend(data.ps2_packet[3], 6); + } else if (data.ps2_packet[3] & PS2M_IMEX_HORIZONTAL_SCROLL) { + // Horizontal scrolling, with 6 bits of precision. + z = sign_extend(data.ps2_packet[3], 6); + wheeln = 1; + } else { + // Or 2 extra buttons (4, 5) + if (data.ps2_packet[3] & PS2M_IMEX_BUTTON_4) { + buttons |= INT33_BUTTON_MASK_4TH; + } + if (data.ps2_packet[3] & PS2M_IMEX_BUTTON_5) { + buttons |= INT33_BUTTON_MASK_5TH; + } + // Plus (vertical) scrolling with 4 bits of precision. + z = sign_extend(data.ps2_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,6 +837,7 @@ 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) { @@ -798,8 +855,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,7 +867,7 @@ 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; @@ -825,11 +881,6 @@ 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) { // Assume the start of a new packet @@ -841,6 +892,11 @@ static void ps2_mouse_handler(uint16_t word0, uint16_t word1, uint16_t word2, ui data.cur_packet_ticks = ticks; } +#if TRACE_PROTO + dprintf("ps2 callback byte %d/%d = %x\n", + 1 + data.cur_packet_bytes, data.packet_size, word0 & 0xFF); +#endif /* TRACE_PROTO */ + data.ps2_packet[data.cur_packet_bytes] = word0; data.cur_packet_bytes++; @@ -930,20 +986,32 @@ static void reset_mouse_hardware() ps2m_enable(false); 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 = 3; +#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,11 +1019,11 @@ 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) { @@ -965,19 +1033,43 @@ static void reset_mouse_hardware() #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 = 1; + 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) { + dputs("ImEx detected"); + data.num_wheels = 2; + data.num_buttons = 5; + + 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. + } + } +#endif + + dprintf("found mouse device id = 0x%hx\n", data.device_id); } 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 */ @@ -1019,10 +1111,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 } @@ -1041,7 +1129,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 +1137,11 @@ static void reset_mouse_state() data.button[i].released.last.x = 0; data.button[i].released.last.y = 0; } - data.wheel_delta = 0; + for (i = 0; i < MAX_WHEELS; i++) { + data.wheel[i].delta = 0; + data.wheel[i].last.x = 0; + data.wheel[i].last.y = 0; + } data.cursor_visible = false; data.cursor_pos.x = 0; data.cursor_pos.y = 0; @@ -1058,12 +1150,12 @@ static void reset_mouse_state() } /** 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; } /** Return (in the appropiate registers) the desired button press counter and afterwards reset it. */ @@ -1101,7 +1193,7 @@ static void int33_handler(union INTPACK r) reset_mouse_hardware(); reset_mouse_state(); r.w.ax = INT33_MOUSE_FOUND; - r.w.bx = NUM_BUTTONS; + r.w.bx = data.num_buttons; break; case INT33_SHOW_CURSOR: if (data.hidden_count > 0) data.hidden_count--; @@ -1125,9 +1217,11 @@ 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.usewheelapi && data.num_wheels > 0) { + r.h.bh = data.wheel[0].delta; + data.wheel[0].delta = 0; + r.x.si = data.wheel[1].delta; + data.wheel[1].delta = 0; } #endif break; @@ -1151,18 +1245,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.usewheelapi && 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 +1265,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.usewheelapi && 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 +1320,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; @@ -1287,7 +1383,7 @@ static void int33_handler(union INTPACK r) } 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; @@ -1316,8 +1412,15 @@ static void int33_handler(union INTPACK r) 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: @@ -1366,7 +1469,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"); diff --git a/mousetsr.h b/mousetsr.h index 43fccc1..67d6ec4 100644 --- a/mousetsr.h +++ b/mousetsr.h @@ -34,8 +34,10 @@ #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 /** Trace mouse events verbosily. */ #define TRACE_EVENTS 0 /** Trace (noisy) API calls. */ @@ -57,8 +59,11 @@ /** Maximum number of 55ms ticks that may pass between two bytes of the same PS/2 packet */ #define MAX_PS2_PACKET_DELAY 2 -/** Number of buttons reported back to user programs. */ -#define NUM_BUTTONS 3 +/** Maximum number of buttons supported by this driver. */ +#define MAX_BUTTONS 5 + +/** Maximum number of wheels supported. */ +#define MAX_WHEELS 2 /** Size of int33 graphic cursor shape definitions. */ #define GRAPHIC_CURSOR_WIDTH 16 @@ -110,9 +115,13 @@ typedef struct tsrdata { struct point screen_granularity; // Detected mouse hardware & status + /** Current negotiated PS/2 device_id */ + uint8_t device_id; + /** Number of buttons of current device. */ + uint8_t num_buttons; #if USE_WHEEL - /** Whether the current mouse has a wheel (and support is enabled). */ - bool haswheel; + /** Number of wheels of current device. */ + uint8_t num_wheels; #endif /** Packet size that the BIOS is currently using. Either 1 (streaming) or 3 (plain). */ uint8_t bios_packet_size; @@ -168,18 +177,21 @@ 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 { struct point last; 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; diff --git a/mousmain.c b/mousmain.c index f457adf..7198b56 100644 --- a/mousmain.c +++ b/mousmain.c @@ -36,12 +36,15 @@ static nl_catd cat; #if USE_WHEEL -static void detect_wheel(LPTSRDATA data) +static bool detect_wheel() { // 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()) { + if (ps2m_detect_imps2()) { printf(_(1, 0, "Wheel mouse found and enabled\n")); + return true; + } else { + return false; } } @@ -51,20 +54,20 @@ static int set_wheel(LPTSRDATA data, bool enable) data->usewheel = enable; if (data->usewheel) { - detect_wheel(data); - if (!data->haswheel) { + if (detect_wheel()) { 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; +return 0; } 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; } @@ -249,7 +252,7 @@ static int configure_driver(LPTSRDATA data) dlog_init(); // Check for PS/2 mouse BIOS availability - if ((err = ps2m_init(PS2M_PACKET_SIZE_PLAIN))) { + if ((err = ps2m_init(PS2M_PACKET_SIZE_STD))) { fprintf(stderr, _(3, 8, "Cannot init PS/2 mouse BIOS, err=%d\n"), err); // Can't do anything without PS/2 return err; @@ -260,7 +263,7 @@ static int configure_driver(LPTSRDATA data) data->usewheel = true; data->wheel_up_key = 0; data->wheel_down_key = 0; - detect_wheel(data); + detect_wheel(); // Prints message if wheel found #endif #if USE_INTEGRATION 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..b756585 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 0x70 #endif // VERSION_H -- cgit v1.2.3