title "Mouse detection" ;++ ; ; Copyright (c) 1989 Microsoft Corporation ; ; Module Name: ; ; mouse.asm ; ; Abstract: ; ; This module implements the assembley code necessary to determine ; various mouse in the system. ; ; Author: ; ; Shie-Lin Tzong (shielint) 10-Dec-1991. ; Most of the code is taken from win31 setup code(with modification.) ; ; Environment: ; ; x86 Real Mode. ; ; Revision History: ; ; ;-- .xlist include mouse.inc .list .386 extrn _Empty8042:proc extrn Write8042:proc extrn ReadKeyboard:proc extrn _ComPortAddress:word extrn _DisableSerialMice:word extrn _FastDetect:byte _DATA SEGMENT PARA USE16 PUBLIC 'DATA' LATLSBSave db ? LATMSBSave db ? LCRSave db ? MCRSave db ? IERSave db ? fSingle8259 db 0 DWFinalCount dw 2 dup (0) DWCurrCount dw 2 dup (0) NextComPort dw 0 ; Offset into ComPortAddress[] MouseInfo MouseInformation <0, 0, 0FFFFh, 0FFFFh, 0> ; ; MouseDetected is used to indicate if any mouse has been detected. ; MouseDetected dw 0 ; initialize to no InPortIoBase dw 0 ; The Base addr for inport mouse _DATA ends _TEXT SEGMENT PARA USE16 PUBLIC 'CODE' ASSUME CS: _TEXT, DS:_DATA, SS:NOTHING ;++ ; ; USHORT ; LookForPS2Mouse ( ; VOID ; ) ; ; Routine Description: ; ; This function determines mouse type in the system. ; ; Arguments: ; ; None. ; ; Return Value: ; ; (eax): mouse Id. ; ;-- public _LookForPS2Mouse _LookForPS2Mouse proc near push bx push si push di mov si, offset MouseInfo lea si, [si].DeviceId call _Empty8042 int 11h test ax, 4 ; is bit 2 set? jz No_PS2_Mouse ; No, no PS/2 mouse. xor di, di ; ; Shortcut the rest of the detection and mouse reset if fast detect is set. ; cmp _FastDetect, 0 jne short Is_PS2_Mouse ; ; Old Olivetti M400-60 and M400-40 will have trouble reading floppy ; and hard disk if the following call is made . ; mov ax, 0c201h ; reset PS/2 mouse int 15h jc short No_PS2_Mouse jmp short Is_PS2_Mouse mov bh, 03 ; Packet size = 3 bytes mov ax, 0c205h ; init point device interface int 15h jc short No_PS2_Mouse mov ax, 0c201h ; reset PS/2 mouse int 15h jc short No_PS2_Mouse call _Empty8042 ; ; The following sequence of Int 15h calls will determine if a Logitech ; PS/2 mouse is present. This information was obtained from Logitech. ; mov ax,0C203h ; Set resolution to 1 cnt/mm mov bh,0h int 15h jc Is_PS2_Mouse mov ax,0C206h ; Set scaling to 1:1 mov bh,1h int 15h jc Is_PS2_Mouse mov ax,0C206h ; Set scaling to 1:1 mov bh,1h int 15h jc Is_PS2_Mouse mov ax,0C206h ; Set scaling to 1:1 mov bh,1h int 15h jc Is_PS2_Mouse mov ax,0C206h ; Get status mov bh,0h int 15h jc Is_PS2_Mouse or cl,cl ; Is resolution 1 cnt/mm? jz Is_PS2_Mouse ; Yes, then not a Logitech. ; ; If cl is not zero (i.e. 1 cnt/mm) then it is the number of buttons ; and we've found a Logitech 3-button PS/2 mouse ; LT_PS2_Mouse: mov ax,LT_MOUSE + PS2_MOUSE jmp short PS2MouseFound Is_PS2_Mouse: mov ax,MS_MOUSE + PS2_MOUSE jmp short PS2MouseFound No_PS2_Mouse: mov bx, 0 jmp ExitPs2Mouse PS2MouseFound: ; ; Set mouse type and subtype to mouse info structure ; mov bx, offset MouseInfo mov [bx].MouseSubtype, al mov [bx].MouseType, ah mov [bx].MouseIrq, 12 mov [bx].MousePort, 0ffffh mov [bx].DeviceIdLength, di mov MouseDetected, bx ExitPs2Mouse: ; ; Drain 8042 input buffer and leave leave pointing device disabled. ; We don't want user moves the mouse and hangs the system. ; call _Empty8042 mov ax, bx pop di pop si pop bx ret _LookForPS2Mouse endp ;++ ; ; USHORT ; LookForInportMouse ( ; VOID ; ) ; ; Routine Description: ; ; This function determines mouse type in the system. ; ; Arguments: ; ; None. ; ; Return Value: ; ; (eax): mouse Id. ; ;-- public _LookForInportMouse _LookForInportMouse proc near push bx mov dx,INPORT_FIRST_PORT + 2 ; Get address of ID register. inport_try_again: call TestForInport ; Does an InPort exist at this address? jnc inport_found ; No carry ! Inport found ! sub dx,4 ; Nope, try the next possible port. cmp dx,INPORT_LAST_PORT + 2 jae inport_try_again mov ax, 0 ; Fail to detect inport mouse jmp short no_inport inport_found: ; ; Set mouse type and subtype to mouse info structure ; mov ax,MS_MOUSE + INPORT_MOUSE mov cx, dx sub cx, 2 mov bx, offset MouseInfo mov [bx].DeviceIdLength, 0 mov [bx].MouseSubtype, al mov [bx].MouseType, ah mov [bx].MousePort, cx mov InportIoBase, cx mov MouseDetected, bx lea ax, [bx].MouseIrq push ax push cx ; Current Port call _InportMouseIrqDetection add sp, 4 mov ax, offset MouseInfo no_inport: pop bx ret _LookForInportMouse endp ;++ ; ; USHORT ; LookForBusMouse ( ; VOID ; ) ; ; Routine Description: ; ; This procedure will attempt to find a bus mouse adaptor in the system ; and will return the results of this search. ; ; Arguments: ; ; None. ; ; Return Value: ; ; (ax) = Mouse ID. ; ;-- public _LookForBusMouse _LookForBusMouse proc near ; ; If We already found Inport mouse and its IO base is 23ch, it is ; impossible to have a BUS mouse. ; cmp InportIoBase, BUS_MOUSE_BASE jne short @f mov ax, 0 ret @@: push bx ; ; We determine if the bus mouse adaptor is present by attempting to ; program the 8255A, and then seeing if we can write a value out to ; Port B on the 8255A and get that value back. If we can, we assume ; that we have a bus mouse adaptor. ; mov dx,BUS_INIT ; Get address of 8255A control port. mov al,BUS_INIT_VALUE ; Get proper value. DelayOut ; Set up 8255A. mov ax,0A5A5h ; Get a signature byte. address BUS_SIG BUS_INIT ; Get address of Port B. DelayOut ; Set Port B with signature. DelayIn ; Read back Port B. cmp al,ah ; Does it match signature byte? jne No_Bus_Mouse ; Nope - no bus mouse adaptor mov ax,MS_MOUSE + BUS_MOUSE jmp short Bus_Mouse_Found No_Bus_Mouse: mov ax, 0 ; No Bus Mouse detected jmp short Bus_Exit Bus_Mouse_Found: ; ; Set mouse type and subtype to mouse info structure ; mov dx, BUS_MOUSE_BASE mov bx, offset MouseInfo mov [bx].DeviceIdLength, 0 mov [bx].MouseSubtype, al mov [bx].MouseType, ah mov [bx].MousePort, dx mov MouseDetected, bx call BusMouseIrqDetection mov [bx].MouseIrq, ax ; if (ax) = 0xffff, no irq detected mov ax, offset MouseInfo ; return MouseInfor Bus_Exit: pop bx ret _LookForBusMouse endp ;++ ; ; USHORT ; BusMouseIrqDetection ( ; USHORT Port ; ) ; ; Routine Description: ; ; This procedure will attempt to find the irq level associated with the ; Bus mouse in the machine. ; ; Arguments: ; ; (dx) = Bus mouse base I/O port. ; ; Return Value: ; ; (ax) = Irq level. if (ax)= 0xffff, detection failed. ; ;-- BusMouseIrqDetection proc near push bx add dx, 2 ; use adaptor control port in al,dx ; Get irq 2-5 states IOdelay mov ah,al ; Save states mov cx,10000 ; Set loop count xor bh,bh ; Clear changes buffer @@: in al,dx ; Get current states of irq 2-5 IOdelay xor ah,al ; Compare with last state or bh,ah ; Mark any changes mov ah,al ; Previous := current state loop @B ; Keep looking mov ax, 0ffffh or bh,bh ; Any irq found? jz short BusIntExit ; Branch if no interrupt was found BusIntFound: mov ax,5 ; Assume irq5 test bh,0001b ; Is it off? jnz short BusIntExit ; Yes..have irq5 mov ax,2 ; Assume irq2 test bh,1000b jnz short BusIntExit inc ax ; Try irq3 test bh,0100b jnz short BusIntExit inc ax ; Must be irq4 BusIntExit: ; ax contains the IRQ number pop bx ret BusMouseIrqDetection endp ;++ ; ; USHORT ; LookForSerialMouse ( ; VOID ; ) ; ; Routine Description: ; ; This procedure will attempt to find a serial mouse adaptor in the system ; and will return the results of this search. ; ; Arguments: ; ; None. ; ; Return Value: ; ; (ax) = Mouse ID. ; ;-- public _LookForSerialMouse _LookForSerialMouse proc near push di push bx mov di, NextComPort ; Get untested comport cmp di, 8 ; Have we over the comport limit? jae short No_Serial_Mouse ; if above or e, yes, exit serial_try_again: mov cx, di mov al, 1 shr cx, 1 inc cx shl ax, cl test _DisableSerialMice, ax ; Should we skip this com port? jnz short serial_next_port ; yes, try next one. mov dx, _ComPortAddress[di] ; Get base address of COM port to test. or dx,dx ; Does this port exist? jz serial_next_port ; No, try next one. serial_test_port: ; ; The comport address is initialized by com detection routine. if the port ; value is not zero, it means that the port exist. ; call TestForSerial ; Is a serial mouse attached to port? cmp ax,NO_MOUSE jne Serial_Mouse_Found ; Yes! found a serial mouse serial_next_port: ; No serial mouse on this COM port. add di,2 ; move to the next possible port cmp di,8 ; Are we over com limit? jb serial_try_again ; if b, no, go test it. mov NextComport, di No_Serial_Mouse: mov ax, 0 ; No serial mouse detected jmp short SerialMouseExit Serial_Mouse_Found: mov NextComport, di add NextComport, 2 ; Next comport to test shr di, 1 ; divide di by 2 ; ; Set mouse type and subtype to mouse info structure ; mov bx, offset MouseInfo mov [bx].DeviceIdLength, 0 cmp ax, MS_MOUSE + SERIAL_MOUSE_WITH_WHEEL jnz short @f mov [bx].DeviceIdLength, 7 @@: mov [bx].MouseSubtype, al mov [bx].MouseType, ah mov [bx].MousePort, di mov [bx].MouseIrq, 0ffffh mov MouseDetected, bx mov ax, bx SerialMouseExit: pop bx pop di ret _LookForSerialMouse endp ;++ ; ; BOOLEAN ; TestForInport ( ; USHORT Port ; ) ; ; Routine Description: ; ; This procedure will attempt to find an InPort mouse at the given base ; I/O address. Note that if an InPort is found, it will be left ; in a state where it will not be generating any interrupts. ; ; Arguments: ; ; Port (DX) - I/O address of Inport identification register. ; ; Return Value: ; ; NC - An Inport was found ; CY - No Inport was found ; ;-- TestForInport PROC NEAR push bx push si ; ; Since the identification register alternates between returning back ; the Inport chip signature and the version/revision, if we have an ; InPort chip, the chip signature will be read in one of the following ; two reads. If it isn't, we do not have an InPort chip. ; mov bl,INPORT_ID in al,dx ; Read ID register. cmp al,bl ; Is value the InPort chip signature? je possible_inport ; Yes, go make sure we have an InPort. in al,dx ; Read ID register again. cmp al,bl ; Is value the InPort chip signature? jne inport_not_found ; No, return error ; ; At this point, we managed to read the InPort chip signature, so we have ; a possible InPort chip. The next read from the ID register will ; return the version/revision. We then make sure that the ID register ; alternates between the chip signature and this version/revision. If ; it does, we have an InPort chip. ; possible_inport: in al,dx ; Read version/revision. mov ah,al ; Save it. mov cx,5 ; Test ID register 5 times. inport_check: in al,dx ; Read ID register. cmp al,bl ; Make sure it is the chip signature. jne inport_not_found ; If not, we don't have an InPort chip. in al,dx ; Read ID register. cmp al,ah ; Make sure version/revision is same. jne inport_not_found ; If not, we don't have an InPort chip. loop inport_check ; Test desired number of times. clc pop si pop bx ret ; ; At this point, we know we have an InPort chip. ; inport_not_found: ; We don't have an InPort chip. stc ; Show failure. pop si pop bx ret ; Return to caller. TestForInport ENDP ;++ ; ; BOOLEAN ; TestForSerial ( ; USHORT Port ; ) ; ; Routine Description: ; ; This procedure will attempt to find a serial mouse adaptor in the system ; and will return the results of this search. ; ; Arguments: ; ; (dx) = Port Address. ; ; Return Value: ; ; (ax) = Mouse ID. ; ;-- TestForSerial PROC NEAR call SaveCOMSetting call SetupCOMForMouse ; Set up COM port to talk to mouse. mov cx,SHORTDELAY ; Use a short delay time. call ResetSerialMouse ; Reset mouse to see if it is there. cmp ax,NO_MOUSE jne TFS_Found ; ; If a mouse has been detected, most likely there won't be second mouse. ; so we don't test for LONGDELAY to save some time ; cmp MouseDetected, 0 jne short @f mov cx,LONGDELAY ; Maybe the mouse is just slow. call ResetSerialMouse ; Reset mouse to see if it is there. cmp ax,NO_MOUSE jne TFS_Found @@: call TestForLogitechSerial ; Maybe it's a Logitech Series C TFS_Found: push ax ; Save return value call RestoreCOMSetting pop ax ret TestForSerial ENDP ;++ ; ; VOID ; SaveCOMSetting ( ; USHORT Port ; ) ; ; Routine Description: ; ; This procedure will save the current state of the COM port given. ; ; Arguments: ; ; Port (DX) - Base address of COM port. ; ; Return Value: ; ; None. ; ;-- SaveCOMSetting PROC NEAR push dx ; Save base I/O address. address LCR RXB ; Get address of Line Control Register. DelayIn ; Get current contents. mov [LCRSave],al ; Save them. or al,LC_DLAB ; Set up to access divisor latches. DelayOut address LATMSB LCR ; Get address of high word of divisor DelayIn ; latch and save its current contents. mov [LATMSBSave],al address LATLSB LATMSB ; Get address of low word of divisor DelayIn ; latch and save its current contents. mov [LATLSBSave],al address LCR LATLSB ; Get address of Line Control Register mov al,[LCRSave] ; and disable access to divisor. and al,NOT LC_DLAB DelayOut address MCR LCR ; Get address of Modem Control Register DelayIn ; and save its current contents. mov [MCRSave],al address IER MCR ; Get address of Interrupt Enable Reg- DelayIn ; ister and save its current contents. mov [IERSave],al pop dx ; Restore base I/O address. ret SaveCOMSetting ENDP ;++ ; ; VOID ; RestoreCOMSetting ( ; USHORT Port ; ) ; ; Routine Description: ; ; This procedure will restore the current state of the COM port given. ; ; Arguments: ; ; Port (DX) - Base address of COM port. ; ; Return Value: ; ; None. ; ;-- RestoreCOMSetting PROC NEAR push dx ; Save base I/O address. address LCR RXB ; Get address of Line Control Register. mov al,LC_DLAB ; Set up to access divisor latches. DelayOut address LATMSB LCR ; Get address of high word of divisor mov al,[LATMSBSave] ; and restore it. DelayOut address LATLSB LATMSB ; Get address of low word of divisor mov al,[LATLSBSave] ; and restore it. DelayOut address LCR LATLSB ; Get address of Line Control Register mov al,[LCRSave] ; and restore it, disabling access to and al,NOT LC_DLAB ; the divisor latches. DelayOut address MCR LCR ; Get addres of Modem Control Register mov al,[MCRSave] ; and restore it. DelayOut address IER MCR ; Get address of Interrupt Enable Reg- mov al,[IERSave] ; ister and restore it. DelayOut pop dx ; Restore base I/O address. ret RestoreCOMSetting ENDP ;++ ; ; VOID ; SetupCOMForMouse ( ; USHORT Port ; ) ; ; Routine Description: ; ; This procedure will set up the given COM port so that it can talk to ; a serial mouse. ; ; Arguments: ; ; Port (DX) - Base address of COM port to set up. ; ; Return Value: ; ; COM port set up, all interrupts disabled at COM port ; ;-- SetupCOMForMouse PROC NEAR push dx ; Save base I/O address. mov cx, 60h call SetBaudRate address LCR RXB mov al,LC_BITS7 + LC_STOP1 + LC_PNONE DelayOut ; Set 7,n,1; disable access to divisor. address IER LCR ; Get address of Int. Enable Register xor al,al ; Disable all interrupts at the COM DelayOut ; port level. address LSR IER ; Get address of Line Status Reg. DelayIn ; Read it to clear any errors. pop dx ; Restore base I/O address ret SetupCOMForMouse ENDP ;++ ; ; USHORT ; ResetSerialMouse ( ; USHORT Port, ; USHORT Delay ; ) ; ; Routine Description: ; ; This procedure will reset a serial mouse on the given COM port and will ; return an indication of whether a mouse responded or not. ; ; The function now also checks for the presence of a 'B' as well as an ; 'M' to determine the presence of a pointing device. Also, if the 'M' is ; followed by a '3' the serial mouse is a Logitech. ; ; Mouse returns M ; Ballpoint returns B ; ; Arguments: ; ; Port (DX) - Base I/O address of COM port to use ; Delay (CX) - Number of msecs to use for delays ; ; Return Value: ; ; (ax) = Mouse Type. ; ;-- ResetSerialMouse PROC NEAR push dx ; Save environment. push si push di push es address IER RXB ; Get address of Interrupt Enable Reg. DelayIn ; Get current contents of IER and push ax ; save them. push dx ; Save address of IER. xor al,al ; Disable all interrupts at the DelayOut ; COM port level. address MCR IER ; Get address of Modem Control Reg. mov al,MC_DTR ; Set DTR active; RTS, OUT1, and OUT2 DelayOut ; inactive. This powers down mouse. push cx ; Save amount of time to delay. call SetupForWait ; Set up BX:CX and ES:DI properly for assume es:nothing ; upcoming delay loop. address RXB MCR ; Get address of Receive Buffer. ; ; Now, we wait the specified amount of time, throwing away any stray ; data that we receive. This gives the mouse time to properly reset ; itself. ; rsm_waitloop: in al, dx ; Read and ignore any stray data. call IsWaitOver ; Determine if we've delayed enough. jnc rsm_waitloop ; If not, keep waiting. ; ; Wait is over. ; address LSR RXB ; Get address of Line Status Reg. DelayIn ; Read it to clear any errors. address MCR LSR ; Get address of Modem COntrol Reg. mov al,MC_DTR+MC_RTS ; Set DTR, RTS, and OUT2 active ; OUT1 inactive. DelayOut ; This powers up the mouse. pop cx ; Get amount of time to delay. call SetupForWait ; Set up BX:CX and ES:DI properly for assume es:nothing ; the upcoming delay loop. ; ; We give the mouse the specified amount of time to respond by sending ; us an M. If it doesn't, or we get more than 5 characters that aren't ; an M, we return a failure indication. ; address LSR MCR ; Get address of Line Status Reg. mov si, 5 ; Read up to 5 chars from port. mov bl,'3' ; '3' will follow 'M' on Logitech. mov bh,'B' ; 'B' for BALLPOINT mov ah,'M' ; Get an M. (We avoid doing a cmp al,M ; because the M could be left floating ; due to capacitance.) rsm_getchar: DelayIn ; Get current status. test al,LS_DR ; Is there a character in Receive Buff? jnz rsm_gotchar ; Yes! Go and read it. call IsWaitOver ; No, determine if we've timed out. jnc rsm_getchar ; Haven't timed out; keep looking. mov bx,NO_MOUSE jmp rsm_leave ; Timed out. Leave with NO_MOUSE. rsm_gotchar: address RXB LSR ; Get address of Receive Buffer. DelayIn ; Get character that was sent to us. cmp al,ah ; Is it an M? jne check_for_b ; ; We received an 'M', now wait for next character to see if it is a '3'. ; mov cx,1 ; Wait between 55.5 and 111ms for call SetupForWait ; next character. address LSR RXB rsm_waitfor3: DelayIn ; Get current status. test al,LS_DR ; Is there a character in Receive Buff? jnz rsm_gotchar3 ; Yes! Go and read it. call IsWaitOver ; No, determine if we've timed out. jnc rsm_waitfor3 ; Haven't timed out; keep looking. ; ; Not a Logitech - must be a standard Microsoft compatible serial mouse. ; jmp rsm_notLT rsm_gotchar3: address RXB LSR ; Get address of Receive Buffer. DelayIn ; Get character that was sent to us. cmp al,bl ; Is it a 3? jne short rsm_check_for_z mov bx,LT_MOUSE + SERIAL_MOUSE ; Yes, we've found a Logitech M+ jmp rsm_leave ; series, 3 button mouse rsm_check_for_z: ; ; Determine if this is Microsoft mouse with wheel. ; 'M', 'Z', 0x40, 0x00, 0x00, 0x00, PnP String ; cmp al, 'Z' jnz rsm_notLT ; ; Check for 0x40, 0x00, 0x00, 0x00 ; mov ebx, 040h mov cx, 4 address LSR RXB rsm_get_byte: push cx mov cx,1 ; Wait between 55.5 and 111ms for call SetupForWait ; next character. @@: DelayIn ; Get current status. test al,LS_DR ; Is there a character in Receive Buff? jnz short @f ; Yes! Go and read it. call IsWaitOver ; No, determine if we've timed out. jnc short @b ; Haven't timed out; keep looking. jmp rsm_notMZ @@: address RXB LSR ; Get address of Receive Buffer. DelayIn ; Get character that was sent to us. cmp al,bl ; Is it a MS wheel? jnz rsm_notMZ shr ebx, 8 address LSR RXB pop cx sub cx, 1 jnz rsm_get_byte ; ; Next read PnP string for the MS wheel mouse ; First skip 3 bytes: 08 + 2-byte Rev number ; mov cx, 3 rsm_get_byte1: push cx mov cx,1 ; Wait between 55.5 and 111ms for call SetupForWait ; next character. @@: DelayIn ; Get current status. test al,LS_DR ; Is there a character in Receive Buff? jnz short @f ; Yes! Go and read it. call IsWaitOver ; No, determine if we've timed out. jnc short @b ; Haven't timed out; keep looking. jmp rsm_notMZ @@: address RXB LSR ; Get address of Receive Buffer. DelayIn ; Get character that was sent to us. address LSR RXB pop cx sub cx, 1 jnz rsm_get_byte1 ; ; Next read 7 bytes PnpDevice id mov si, offset MouseInfo lea si, [si].DeviceId mov cx, 7 rsm_get_byte2: push cx mov cx,1 ; Wait between 55.5 and 111ms for call SetupForWait ; next character. @@: DelayIn ; Get current status. test al,LS_DR ; Is there a character in Receive Buff? jnz short @f ; Yes! Go and read it. call IsWaitOver ; No, determine if we've timed out. jnc short @b ; Haven't timed out; keep looking. jmp rsm_notMZ @@: address RXB LSR ; Get address of Receive Buffer. DelayIn ; Get character that was sent to us. mov [si], al inc si address LSR RXB pop cx sub cx, 1 jnz rsm_get_byte2 mov byte ptr [si], 0 ; add device id terminated null mov bx, MS_MOUSE + SERIAL_MOUSE_WITH_WHEEL jmp short rsm_leave ; We still have a standard serial mouse. rsm_notMZ: pop cx rsm_notLT: mov bx,MS_MOUSE + SERIAL_MOUSE ; We didn't get the '3' after the 'M' jmp short rsm_leave ; We still have a standard serial mouse. check_for_b: cmp al,bh ; Is it a B? jne rsm_next_char mov bx,MS_BALLPOINT + SERIAL_MOUSE ; We've found a BallPoint Mouse jmp short rsm_leave rsm_next_char: address LSR RXB ; Oh well. Get address of LSR again. dec si ; Have we read 5 chars yet? jnz rsm_getchar ; Nope, we'll give him another try. ; ; We've read many characters - No a single 'M' or 'B' in the lot. ; mov bx,NO_MOUSE rsm_leave: pop dx ; Get address of IER. pop ax ; Get old value of IER. DelayOut ; Restore IER. pop es ; Restore environment. assume es:nothing pop di pop si pop dx mov ax,bx ; Set return value. ret ResetSerialMouse ENDP ;++ ; ; VOID ; SetupForWait ( ; USHORT WaitTime ; ) ; ; Routine Description: ; ; This procedure accepts the number of milliseconds that we will want ; to delay for and will set things up for the wait. ; ; Arguments: ; ; (CX) = Number of clock ticks to wait for. ; ; Return Value: ; ; None. ; ;-- SetupForWait PROC NEAR push ax ; Do your saving ! push es xor ax,ax mov es,ax ; Point to 40:6C = 0:46C cli mov ax,es:[LW_ClockTickCount+2] mov [DWFinalCount+2],ax ; Save ending time (HiWord) mov ax,es:[LW_ClockTickCount] ; Get tick count in AX. sti add ax,cx ; [Current + delay] = delay ends. mov [DWFinalCount],ax ; Save ending time (LoWord) jnc SFW_End inc [DWFinalCount+2] SFW_End: pop es ; Restore now ! pop ax ret SetupForWait ENDP ;++ ; ; BOOLEAN ; IsWaitOver ( ; VOID ; ) ; ; Routine Description: ; ; This procedure accepts the current time and the ending time and ; return and indication of whether the current time is past ; the ending time. ; ; Arguments: ; ; None. ; ; Return Value: ; ; carry clear Current time is not past ending time ; carry set Current time is past ending time ; ;-- IsWaitOver PROC NEAR if 0 push ax ; Preserve AX push es ; Preserve ES xor ax,ax mov es,ax ; Point to 40:6C = 0:46C cli mov ax,es:[LW_ClockTickCount] mov [DWCurrCount],ax ; Save current time (LoWord) mov ax,es:[LW_ClockTickCount+2] ; Get tick count in AX. sti cmp [DWFinalCount+2],ax ; Compare HiWords ja WaitNotOver ; Carry will be clear if wait ; is not over. mov ax,es:[LW_ClockTickCount] ; Compare Lowords cmp [DWFinalCount],ax ; This will set CY accordingly WaitNotOver: pop es ; Restore ES pop ax ; Restore AX ret else push ax ; Preserve AX push es ; Preserve ES xor ax,ax mov es,ax ; Point to 40:6C = 0:46C cli mov ax,es:[LW_ClockTickCount] mov [DWCurrCount],ax ; Save current time (LoWord) mov ax,es:[LW_ClockTickCount+2] ; Get tick count in AX. sti cmp [DWFinalCount+2],ax ; Compare HiWords jb WaitExit ; Time is up jne WaitRollCheck ; If not equal check for WaitLowCheck: mov ax,[DWCurrCount] ; Compare Lowords cmp [DWFinalCount],ax ; This will set CY accordingly WaitExit: pop es ; Restore ES pop ax ; Restore AX ret WaitRollCheck: ; If the current time is less than the wait time we must check for ; roll over. There are 18.2 * 60 * 60 * 24 or 0x1800b0 clock ticks in ; a day. At midnight the counter rolls over to zero. cmp ax,0 jne WaitExit ; If current HiWord is not 0, ; no roll over. Exit with ; carry clear. cmp [DWFinalCount+2],18h ; Is Final HiWord 0x18 je short @f ; Yes, check LoWord for wrap. clc ; No, no roll over. Exit with ; carry clear. jmp WaitExit @@: mov ax,[DWFinalCount] ; Get final LoWord sub ax, 0b0h ; Check for wrap jb WaitExit ; No, no roll over. Exit with ; cary set ; At this point we have determined that we have wrapped and that the ; ending time is into the next day. Update the ending time mov [DWFinalCount],ax ; Set final LoWord xor ax,ax mov [DWFinalCount+2],ax ; Zero final HiWord jmp WaitLowCheck ; Check LoWord endif IsWaitOver ENDP ;++ ; ; USHORT ; TestForLogitechSerial ( ; VOID ; ) ; ; Routine Description: ; ; This procedure will detect the presence of a Logitech Series C ; serial mouse is present ; ; Arguments: ; ; (edx) = Port Address ; ; Return Value: ; ; (ax) = Mouse ID. ; ;-- TestForLogitechSerial PROC NEAR push di push bx sub sp, 10 mov bx, sp mov word ptr [bx], 60h ; baud = 1200 mov word ptr [bx + 2], 30h ; baud = 2400 mov word ptr [bx + 4], 18h ; baud = 4800 mov word ptr [bx + 6], 0ch ; baud = 9600 mov word ptr [bx + 8], 0 ; ; Power up the C series mouse. ; ; Set both DTR and RTS to an active state ; If DTR and RTS are already on, the power is on for at least 500ms ; due to the MM serial mouse detection. ; address MCR RXB ; Get address of Modem Control Reg. DelayIn ; Get modem control byte and al, MC_DTR + MC_RTS ; Check DTR and RTS cmp al, MC_DTR + MC_RTS je short @f ; the lines are high already mov al, MC_DTR + MC_RTS ; Set DTR and RTS to an active state DelayOut ; and ... mov cx,9 ; wait for 1/2 second to pwrup mouse call SetupForWait ; Set up BX:CX and ES:DI properly for assume es:nothing ; upcoming delay loop. ; ask for current baud rate lt_waitloop1: call IsWaitOver ; Determine if we've delayed enough. jnc short lt_waitloop1 @@: ; ; Set the line control register to a format that the mouse can ; understand (see below: the line is set after the report rate). ; address LCR MCR ; Get address of Line Control Reg. mov al,LC_BITS8 + LC_STOP1 + LC_PODD DelayOut ; ; Cycle through the different baud rates to detect the mouse. ; mov di, 0 address RXB LCR Tfs_Next_Baud: mov cx, [bx + di] cmp cx, 0 je Tfs110 ; Reach the end of table call SetBaudRate ; Set baud rate ; ; Put the mouse in prompt mode. ; mov cl, 'D' call CSerWriteChar ; ; Set the MM protocol. This way we get the mouse to talk to us in a ; specific format. This avoids receiving errors from the line ; register. ; mov cl, 'S' call CSerWriteChar address LCR RXB ; Get address of Line Control Reg. mov al,LC_BITS8 + LC_STOP1 + LC_PODD DelayOut ; ; Try to get the status byte. ; address RXB LCR mov cl, 's' call CSerWriteChar ; ; Read back the status character. ; mov cx,2 ; Wait at least 55.5 ms for response. call SetupForWait assume es:nothing address LSR RXB lt_waitloop2: ; (dx) = LSR reg DelayIn test al, LS_DR ; Is receiving buffer full? jnz short @f ; Yes, go read it. lt_waitloop21: ; (dx) = LSR reg call IsWaitOver jnc short lt_waitloop2 address RXB LSR jmp short Tfs50 @@: address RXB LSR DelayIn cmp al, 04fh ; al = 4Fh means command understood je short Tfs100 address LSR RXB jmp short lt_waitloop21 Tfs50: add di, 2 jmp Tfs_Next_Baud Tfs100: ; ; Found the C series mouse. Put the mouse back in a default mode. ; The protocol is already set. ; ; ; Set to default baud rate 1200 ; mov cl, '*' call CSerWriteChar mov cl, 'n' call CSerWriteChar ; ; Wait for TX buffer empty ; mov cx, 1 call SetupForWait address LSR RXB @@: DelayIn and al, LS_THRE + LS_TSRE cmp al, LS_THRE + LS_TSRE je short @f ; Wait for TX buffer empty call IsWaitOver jnc short @b @@: address RXB LSR mov cx, 60h ; Set baud rate to 1200 call SetBaudRate ; ; Set mouse to default report rate ; mov cl, 'N' call CSerWriteChar mov ax,LT_MOUSE + SERIAL_MOUSE jmp short lt_leave Tfs110: mov ax,NO_MOUSE lt_leave: add sp, 10 ; clear stack pop bx pop di ret TestForLogitechSerial ENDP ;++ ; ; VOID ; SetBaudRate ( ; USHORT Port, ; USHORT BaudRate ; ) ; ; Routine Description: ; ; This procedure will set up the given COM port so that it can talk to ; a Logitech C series serial mouse. ; ; Arguments: ; ; (DX) = COM Base address of COM port to set up. ; (CX) = Baud Rate ; ; Return Value: ; ; None. ; ;-- SetBaudRate PROC NEAR push dx address LCR RXB ; Get address of Line Control Reg. DelayIn or al,LC_DLAB ; Set up to access divisor latches. DelayOut address LATMSB LCR ; Get address of high word of divisor mov al, ch ; latch and set it with value for DelayOut ; specified baud. address LATLSB LATMSB ; Get address of low word of divisor mov al, cl ; latch and set it with value for DelayOut ; specified baud. address LCR LATLSB ; Get address of Line Control Reg. DelayIn and al, NOT LC_DLAB ; Disable access divisor latches. DelayOut mov cx, 1 call SetupForWait @@: call IsWaitOver jnc short @b pop dx ret SetBaudRate ENDP ;++ ; ; VOID ; CSerWriteChar ( ; USHORT Port, ; UCHAR Command ; ) ; ; Routine Description: ; ; This procedure will write a char/command to logitech C series mouse. ; ; Arguments: ; ; (DX) = COM Base address of COM port to set up. ; (CL) = Command ; ; Return Value: ; ; None. ; ;-- CserWriteChar proc near push cx mov cx, 1 call SetupForWait address LSR RXB @@: DelayIn and al, LS_THRE + LS_TSRE cmp al, LS_THRE + LS_TSRE je short @f ; Wait for TX buffer empty call IsWaitOver jnc short @b @@: address TXB LSR pop ax ; Send command DelayOut ret CserWriteChar endp if 0 ;++ ; ; VOID ; FlushReceiveBuffer ( ; USHORT Port ; ) ; ; Routine Description: ; ; This procedure will flush receive buffer or until time out. ; ; Arguments: ; ; (DX) = COM Base address of COM port to set up. ; ; Return Value: ; ; None. ; ;-- FlushReceiveBuffer proc near mov cx, 5 call SetupForWait @@: address LSR RXB DelayIn test al, LS_DR jz short @f address RXB LSR DelayIn call IsWaitOver jnc short @b ret @@: address RXB LSR ret FlushReceiveBuffer endp endif _TEXT ends end