windows-nt/Source/XPSP1/NT/base/boot/detect/i386/mousea.asm
2020-09-26 16:20:57 +08:00

1615 lines
41 KiB
NASM
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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