331 lines
9.7 KiB
NASM
331 lines
9.7 KiB
NASM
;Copyright (C) 1997-2004 ZSNES Team ( zsknight@zsnes.com / _demo_@zsnes.com )
|
|
;
|
|
;This program is free software; you can redistribute it and/or
|
|
;modify it under the terms of the GNU General Public License
|
|
;as published by the Free Software Foundation; either
|
|
;version 2 of the License, or (at your option) any later
|
|
;version.
|
|
;
|
|
;This program is distributed in the hope that it will be useful,
|
|
;but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
;MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
;GNU General Public License for more details.
|
|
;
|
|
;You should have received a copy of the GNU General Public License
|
|
;along with this program; if not, write to the Free Software
|
|
;Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;32-bit DOS-Mode driver for the Microsoft Sidewinder Gamepad
|
|
;Multi-SW Version 1.5
|
|
;(C) 1997, 1998 Robert William Grubbs, All Rights Reserved
|
|
;Latest revision 1/20/98
|
|
; Driver Source code Include file
|
|
|
|
;C-linkable, rewrote decoder -sardu
|
|
|
|
;Flat memory mode (Protected mode extender required! Tested with DOS32)
|
|
;Tested with TASM 4.0+
|
|
|
|
SECTION .data
|
|
SW1 dd 0 ;SW #1's button status
|
|
SW2 dd 0 ;SW #2's button status
|
|
SW3 dd 0 ;SW #3's button status
|
|
SW4 dd 0 ;SW #4's button status
|
|
SWCount dd 1 ;Tell the driver how many sidewinders are present
|
|
SWSetup dd 0 ;Tell the driver what polling mode to use
|
|
; 0=Interrupts disabled, Multiple OUT statements
|
|
; 1=Interrupts disabled, Single OUT statement
|
|
; 2=Interrupts enabled, Multiple OUT statements
|
|
; 3=Interrupts enabled, Single OUT statement
|
|
|
|
gDump times 100h db 0 ;SW Status dump buffer (space for 256 bytes, uses 200)
|
|
bDump times 80h db 0 ;buffer to hold button data (Modes A and B, all SW)
|
|
SECTION .text
|
|
|
|
;This macro calculates parity for the buttons and compares it to the SW's
|
|
; parity bit. If they don't match, the button data is discarded.
|
|
%macro ParityCheckSW 1
|
|
mov ecx,ebx ;duplicate button status
|
|
xor cl,ch ;
|
|
jpe %%ParChkSW
|
|
mov [%1],ebx ;update button status for SW #n
|
|
%%ParChkSW ;done
|
|
%endmacro
|
|
|
|
;The main subroutine; this is the important one; bow down before it
|
|
;IN: None
|
|
;Out: SWx=buttons (bit 0=null 1=up 2=dn 3=rt 4=lt 5=A 6=B 7=C 8=X)
|
|
; (9=Y 10=Z 11=L 12=R 13=St 14=M 15=Parity)
|
|
;No registers destroyed
|
|
|
|
readSideWinder:
|
|
pushad
|
|
|
|
mov ecx,200 ;dump buffer fill size
|
|
mov ebx,gDump ;initial dump pointer
|
|
mov edx,0201h ;joystick port
|
|
|
|
cmp dword[SWSetup],0
|
|
jne NotSW0
|
|
cli ;Disable interrupts (required to avoid jitter)
|
|
GetSWDataLoop: ;
|
|
out dx,al ;trigger joystick port
|
|
in al,dx ;read SW status byte
|
|
mov [ebx],al ;dump status byte
|
|
inc ebx ;increment dump pointer
|
|
dec ecx
|
|
jnz GetSWDataLoop
|
|
sti ;Re-enable interrupts
|
|
jmp SWPollDone
|
|
|
|
NotSW0:
|
|
cmp dword[SWSetup],1
|
|
jne NotSW1
|
|
cli ;Disable interrupts (required to avoid jitter)
|
|
out dx,al ;trigger joystick port
|
|
GetSWDataLoop1: ;
|
|
in al,dx ;read SW status byte
|
|
mov [ebx],al ;dump status byte
|
|
inc ebx ;increment dump pointer
|
|
dec ecx
|
|
jnz GetSWDataLoop1
|
|
sti ;Re-enable interrupts
|
|
jmp SWPollDone
|
|
|
|
NotSW1:
|
|
cmp dword[SWSetup],2
|
|
jne NotSW2
|
|
GetSWDataLoop2: ;
|
|
out dx,al ;trigger joystick port
|
|
in al,dx ;read SW status byte
|
|
mov [ebx],al ;dump status byte
|
|
inc ebx ;increment dump pointer
|
|
dec ecx
|
|
jnz GetSWDataLoop2
|
|
jmp SWPollDone
|
|
|
|
NotSW2:
|
|
;default all others to SWStatus=3
|
|
out dx,al ;trigger joystick port
|
|
GetSWDataLoop3: ;
|
|
in al,dx ;read SW status byte
|
|
mov [ebx],al ;dump status byte
|
|
inc ebx ;increment dump pointer
|
|
dec ecx
|
|
jnz GetSWDataLoop3
|
|
|
|
SWPollDone:
|
|
|
|
mov ecx,0 ;tick count
|
|
mov esi,1 ;initialize output mask
|
|
mov ebx,0 ;initialize output
|
|
mov edi,0 ;initialize input pointer
|
|
|
|
;My current method of cycle detection is to look for 15 highs in a row on
|
|
; the strobe line. Cycle ends is detected by 15 lows in a row.
|
|
;Mode A has 15 strobes in a cycle, Mode B has 5.
|
|
; Note that the 15 highs/lows for cycle detection may be too high for slow
|
|
; machines. I havn't seen a problem yet, but it may exist...
|
|
;Multiple Sidewinder data complicates things. Each additional SW tags
|
|
; another set of strobes to the cycle, 5 more in mode B, 15 more in mode A.
|
|
; Detecting extra SW gamepad data is fairly simple: count the number of
|
|
; strobes. If it is a multiple of 5, you're in mode B and can divide by 5
|
|
; to get the total number of gamepads. If it's divisible by 15, use mode A.
|
|
; However, this method cannot distinguish between mode A for one SW and mode
|
|
; B for three SW. In that case, the SWCount variable must be set correctly.
|
|
|
|
FindCycle:
|
|
mov al,[gDump+edi] ;get next status byte
|
|
inc edi ;increment input pointer
|
|
cmp edi,200 ;test for end of status block
|
|
je SWNoFind ;if it's the end, quit sub with error
|
|
test al,00010000b ;Check for nonzero bits
|
|
jnz WMFCS1 ;
|
|
xor ecx,ecx ;if zero, reset tick count
|
|
jmp FindCycle ;can't be pre-cycle
|
|
WMFCS1: ;Possibly pre-cycle
|
|
inc ecx ;increment tick count
|
|
cmp ecx,15 ;test for sufficient ticks for cycle start
|
|
jne FindCycle ;if insufficient, get next status byte
|
|
;Yippie! it found a (probable) cycle!
|
|
|
|
mov ebp,0 ;initialize bDump index (strobe count)
|
|
|
|
FindStrobeLow: ;Search for leading edge of data strobe
|
|
mov al,[gDump+edi] ;get next status byte
|
|
inc edi ;increment input pointer
|
|
cmp edi,200 ;test for end of status block
|
|
je SWNoFind ;if it's the end, quit sub with error
|
|
test al,00010000b ;get "strobe" bit
|
|
jnz SHORT FindStrobeLow ;if it isn't zero, we're not there yet
|
|
xor ecx,ecx ;initialize cycle end test count
|
|
|
|
FindStrobeHigh:
|
|
inc ecx ;increment zero count
|
|
cmp ecx,0fh ;is it 15?
|
|
je SWModeCheck ;if so, goto mode check
|
|
mov al,[gDump+edi] ;get next status byte
|
|
inc edi ;increment input pointer
|
|
cmp edi,200 ;test for end of status block
|
|
je SWNoFind ;if it's the end, quit sub with error
|
|
test al,00010000b ;get "strobe" bit
|
|
jz FindStrobeHigh ;if it is zero, we're not there yet
|
|
;if not, we're there! data bit is valid (probably)
|
|
mov [bDump+ebp],al ;preserve data for button decoding
|
|
inc ebp ;increment strobe count/bDump index
|
|
jmp FindStrobeLow ;wait for the next button
|
|
|
|
SMWDone:
|
|
SWNoFind:
|
|
popad
|
|
ret ;return to calling procedure
|
|
|
|
SWModeCheck: ;Check strobe count to identify mode and # of SW
|
|
cmp ebp,15 ;Is it Mode A with 1 Sidewinder or B with 3?
|
|
je ModeA1
|
|
cmp ebp,5 ;Is it Mode B with 1 Sidewinders?
|
|
je ModeB1
|
|
cmp ebp,30 ;Is it Mode A with 2 Sidewinders?
|
|
je ModeA2
|
|
cmp ebp,10 ;Is it Mode B with 2 Sidewinders?
|
|
je near ModeB2
|
|
cmp ebp,45 ;Is it Mode A with 3 Sidewinders?
|
|
je near ModeA3
|
|
cmp ebp,60 ;Is it Mode A with 4 Sidewinders?
|
|
je near ModeA4
|
|
cmp ebp,20 ;Is it Mode B with 4 Sidewinders?
|
|
je near ModeB4
|
|
jmp SHORT SWNoFind ;Any other # of strobes is invalid data
|
|
|
|
ModeB1:
|
|
xor ebp,ebp
|
|
call DoModeB
|
|
ParityCheckSW SW1
|
|
jmp SMWDone
|
|
|
|
ModeA1:
|
|
cmp dword [SWCount],3
|
|
je near ModeB3
|
|
xor ebp,ebp
|
|
call DoModeA
|
|
ParityCheckSW SW1
|
|
jmp SMWDone
|
|
|
|
ModeA2:
|
|
xor ebp,ebp
|
|
call DoModeA
|
|
ParityCheckSW SW1
|
|
mov ebp,15
|
|
call DoModeA
|
|
ParityCheckSW SW2
|
|
jmp SMWDone
|
|
|
|
ModeA3:
|
|
xor ebp,ebp
|
|
call DoModeA
|
|
ParityCheckSW SW1
|
|
mov ebp,15
|
|
call DoModeA
|
|
ParityCheckSW SW2
|
|
mov ebp,30
|
|
call DoModeA
|
|
ParityCheckSW SW3
|
|
jmp SMWDone
|
|
|
|
ModeA4:
|
|
xor ebp,ebp
|
|
call DoModeA
|
|
ParityCheckSW SW1
|
|
mov ebp,15
|
|
call DoModeA
|
|
ParityCheckSW SW2
|
|
mov ebp,30
|
|
call DoModeA
|
|
ParityCheckSW SW3
|
|
mov ebp,45
|
|
call DoModeA
|
|
ParityCheckSW SW4
|
|
jmp SMWDone
|
|
|
|
ModeB2:
|
|
xor ebp,ebp
|
|
call DoModeB
|
|
ParityCheckSW SW1
|
|
mov ebp,5
|
|
call DoModeB
|
|
ParityCheckSW SW2
|
|
jmp SMWDone
|
|
|
|
ModeB3:
|
|
xor ebp,ebp
|
|
call DoModeB
|
|
ParityCheckSW SW1
|
|
mov ebp,5
|
|
call DoModeB
|
|
ParityCheckSW SW2
|
|
mov ebp,10
|
|
call DoModeB
|
|
ParityCheckSW SW3
|
|
jmp SMWDone
|
|
|
|
ModeB4:
|
|
xor ebp,ebp
|
|
call DoModeB
|
|
ParityCheckSW SW1
|
|
mov ebp,5
|
|
call DoModeB
|
|
ParityCheckSW SW2
|
|
mov ebp,10
|
|
call DoModeB
|
|
ParityCheckSW SW3
|
|
mov ebp,15
|
|
call DoModeB
|
|
ParityCheckSW SW4
|
|
jmp SMWDone
|
|
ENDP
|
|
|
|
%macro SWRepeat 1
|
|
mov al,[bDump+ebp+%1]
|
|
shr al,5 ;get upper 3 bits
|
|
shl eax,1+3*%1 ;shift into place
|
|
or ebx,eax ;or into output
|
|
%endmacro
|
|
|
|
DoModeB:
|
|
xor ebx,ebx ;Initialize output
|
|
xor eax,eax
|
|
|
|
SWRepeat 0
|
|
SWRepeat 1
|
|
SWRepeat 2
|
|
SWRepeat 3
|
|
SWRepeat 4
|
|
|
|
xor ebx,0FFFEh
|
|
ret
|
|
|
|
DoModeA:
|
|
xor ebx,ebx ;Clear output
|
|
mov ecx,15 ;bit count
|
|
|
|
ALP:
|
|
mov al,[bDump+ebp]
|
|
inc ebp
|
|
shl al,3
|
|
rcr ebx,1
|
|
dec ecx
|
|
jg ALP
|
|
|
|
shr ebx,16
|
|
xor ebx,0FFFEh
|
|
ret
|
|
|
|
|