Value (binary) | Priority | Switch Position |
---|---|---|
0000 | Highest | Up position on the cooley switch. |
0100 | 7 | Right position on the cooley switch. |
1000 | 6 | Down position on the cooley switch. |
1100 | 5 | Left position on the cooley switch. |
1110 | 4 | Trigger on the joystick. |
1101 | 3 | Leftmost button on the joystick. |
1011 | 2 | Rightmost button on the joystick. |
0111 | Lowest | Middle button on the joystick. |
1111 | - | No buttons currently down. |
This SGDI Switch number: | Maps to this FSP Switch: |
---|---|
0 | Trigger on joystick. |
1 | Left button on joystick. |
2 | Middle button on joystick. |
3 | Right button on joystick. |
4 | Cooley up position. |
5 | Cooley left position. |
6 | Cooley right position. |
7 | Cooley down position. |
.286 page 58, 132 name FSPSGDI title FSPSGDI (CH Products Standard Game Device Interface). ; FSPSGDI.EXE ; ; Usage: ; FSPSDGI {LEFT} ; ; This program loads a TSR which patches INT 15 so arbitrary game programs ; can read the CH Products FlightStick Pro joystick in a portable fashion. wp equ <word ptr> byp equ <byte ptr> ; We need to load cseg in memory before any other segments! cseg segment para public 'code' cseg ends ; Initialization code, which we do not need except upon initial load, ; goes in the following segment: Initialize segment para public 'INIT' Initialize ends ; UCR Standard Library routines which get dumped later on. .xlist include stdlib.a includelib stdlib.lib .list sseg segment para stack 'stack' sseg ends zzzzzzseg segment para public 'zzzzzzseg' zzzzzzseg ends CSEG segment para public 'CODE' assume cs:cseg, ds:nothing Int15Vect dword 0 PSP word ? ; Port addresses for a typical joystick card: JoyPort equ 201h JoyTrigger equ 201h CurrentReading word 0 Pot struc PotMask byte 0 ;Pot mask for hardware. DidCal byte 0 ;Is this pot calibrated? min word 5000 ;Minimum pot value max word 0 ;Max pot value center word 0 ;Pot value in the middle Pot ends Pot0 Pot <1> Pot1 Pot <2> Pot3 Pot <8> ; SwapButtons- 0 if we should use normal flightstick pro buttons, ; 1 if we should swap the left and right buttons. SwapButtons byte 0 ; SwBits- the four bit input value from the Flightstick Pro selects one ; of the following bit patterns for a given switch position. SwBits byte 10h ;Sw4 byte 0 ;NA byte 0 ;NA byte 0 ;NA byte 40h ;Sw6 byte 0 ;NA byte 0 ;NA byte 4 ;Sw 2 byte 80h ;Sw 7 byte 0 ;NA byte 0 ;NA byte 8 ;Sw 3 byte 20h ;Sw 5 byte 2 ;Sw 1 byte 1 ;Sw 0 byte 0 ;NA SwBitsL byte 10h ;Sw4 byte 0 ;NA byte 0 ;NA byte 0 ;NA byte 40h ;Sw6 byte 0 ;NA byte 0 ;NA byte 4 ;Sw 2 byte 80h ;Sw 7 byte 0 ;NA byte 0 ;NA byte 2 ;Sw 3 byte 20h ;Sw 5 byte 8 ;Sw 1 byte 1 ;Sw 0 byte 0 ;NA ; The IDstring address gets passed back to the caller on a testpresence ; call. The four bytes before the IDstring must contain the serial number ; and current driver number. SerialNumber byte 0,0,0 IDNumber byte 0 IDString byte "CH Products:Flightstick Pro",0 byte "Written by Randall Hyde",0 ;============================================================================ ; ; ReadPots- AH contains a bit mask to determine which pots we should read. ; Bit 0 is one if we should read pot 0, bit 1 is one if we should ; read pot 1, bit 3 is one if we should read pot 3. All other bits ; will be zero. ; ; This code returns the pot values in SI, BX, BP, and DI for Pot 0, 1, ; 2, & 3. ; ReadPots proc near sub bp, bp mov si, bp mov di, bp mov bx, bp ; Wait for pots to finish any past junk: mov dx, JoyPort out dx, al ;Trigger pots mov cx, 400h Wait4Pots: in al, dx and al, 0Fh loopnz Wait4Pots ; Okay, read the pots: mov dx, JoyTrigger out dx, al ;Trigger pots mov dx, JoyPort mov cx, 8000h ;Don't let this go on forever. PotReadLoop: in al, dx and al, ah jz PotReadDone shr al, 1 adc si, 0 shr al, 1 adc bp, 0 shr al, 2 adc di, 0 loop PotReadLoop PotReadDone: ret ReadPots endp ;---------------------------------------------------------------------------- ; ; Normalize- BX contains a pointer to a pot structure, AX contains ; a pot value. Normalize that value according to the ; calibrated pot. ; ; Note: DS must point at cseg before calling this routine. assume ds:cseg Normalize proc near push cx ; Sanity check to make sure the calibration process went okay. cmp [bx].Pot.DidCal, 0 je BadNorm mov dx, [bx].Pot.Center cmp dx, [bx].Pot.Min jbe BadNorm cmp dx, [bx].Pot.Max jae BadNorm ; Clip the value if it is out of range. cmp ax, [bx].Pot.Min ja MinOkay mov ax, [bx].Pot.Min MinOkay: cmp ax, [bx].Pot.Max jb MaxOkay mov ax, [bx].Pot.Max MaxOkay: ; Scale this guy around the center: cmp ax, [bx].Pot.Center jb Lower128 ; Scale in the range 128..255 here: sub ax, [bx].Pot.Center mov dl, ah ;Multiply by 128 mov ah, al mov dh, 0 mov al, dh shr dl, 1 rcr ax, 1 mov cx, [bx].Pot.Max sub cx, [bx].Pot.Center jz BadNorm ;Prevent division by zero. div cx ;Compute normalized value. add ax, 128 ;Scale to range 128..255. cmp ah, 0 je NormDone mov ax, 0ffh ;Result must fit in 8 bits! jmp NormDone ; Scale in the range 0..127 here: Lower128: sub ax, [bx].Pot.Min mov dl, ah ;Multiply by 128 mov ah, al mov dh, 0 mov al, dh shr dl, 1 rcr ax, 1 mov cx, [bx].Pot.Center sub cx, [bx].Pot.Min jz BadNorm div cx ;Compute normalized value. cmp ah, 0 je NormDone mov ax, 0ffh ;Result must fit in 8 bits! jmp NormDone BadNorm: sub ax, ax NormDone: pop cx ret Normalize endp assume ds:nothing ;============================================================================ ; INT 15h handler functions. ;============================================================================ ; ; Although these are defined as near procs, they are not really procedures. ; The MyInt15 code jumps to each of these with BX, a far return address, and ; the flags sitting on the stack. Each of these routines must handle the ; stack appropriately. ; ;---------------------------------------------------------------------------- ; BIOS- Handles the two BIOS calls, DL=0 to read the switches, DL=1 to ; read the pots. For the BIOS routines, we'll ignore the cooley ; switch (the hat) and simply read the other four switches. BIOS proc near cmp dl, 1 ;See if switch or pot routine. jb Read4Sw je ReadBIOSPots pop bx jmp cs:Int15Vect ;Let someone else handle it! Read4Sw: push dx mov dx, JoyPort in al, dx shr al, 4 mov bl, al mov bh, 0 cmp cs:SwapButtons, 0 je DoLeft2 mov al, cs:SwBitsL[bx] jmp SBDone DoLeft2: mov al, cs:SwBits[bx] SBDone: rol al, 4 ;Put Sw0..3 in upper bits and make not al ; 0=switch down, just like game card. pop dx pop bx iret ReadBIOSPots: pop bx ;Return a value in BX! push si push di push bp mov ah, 0bh call ReadPots mov ax, si mov bx, bp mov dx, di sub cx, cx pop bp pop di pop si iret BIOS endp ;---------------------------------------------------------------------------- ; ; ReadPot- On entry, DL contains a pot number to read. ; Read and normalize that pot and return the result in AL. assume ds:cseg ReadPot proc near ;;;;;;;;;; push bx ;Already on stack. push ds push cx push dx push si push di push bp mov bx, cseg mov ds, bx cmp dl, 0 jne Try1 mov ah, Pot0.PotMask call ReadPots lea bx, Pot0 mov ax, si call Normalize jmp GotPot Try1: cmp dl, 1 jne Try3 mov ah, Pot1.PotMask call ReadPots lea bx, Pot1 mov ax, bp call Normalize jmp GotPot Try3: cmp dl, 3 jne BadPot mov ah, Pot3.PotMask call ReadPots lea bx, Pot3 mov ax, di call Normalize jmp GotPot BadPot: sub ax, ax ;Question: Should we pass this on ; or just return zero? GotPot: pop bp pop di pop si pop dx pop cx pop ds pop bx iret ReadPot endp assume ds:nothing ;---------------------------------------------------------------------------- ; ; ReadRaw- On entry, DL contains a pot number to read. ; Read that pot and return the unnormalized result in AL. assume ds:cseg ReadRaw proc near ;;;;;;;;;; push bx ;Already on stack. push ds push cx push dx push si push di push bp mov bx, cseg mov ds, bx cmp dl, 0 jne Try1 mov ah, Pot0.PotMask call ReadPots mov ax, si jmp GotPot Try1: cmp dl, 1 jne Try3 mov ah, Pot1.PotMask call ReadPots mov ax, bp jmp GotPot Try3: cmp dl, 3 jne BadPot mov ah, Pot3.PotMask call ReadPots mov ax, di jmp GotPot BadPot: sub ax, ax ;Just return zero. GotPot: pop bp pop di pop si pop dx pop cx pop ds pop bx iret ReadRaw endp assume ds:nothing ;---------------------------------------------------------------------------- ; Read4Pots- Reads pots zero, one, two, and three returning their ; values in AL, AH, DL, and DH. Since the flightstick ; Pro doesn't have a pot 2 installed, return zero for ; that guy. Read4Pots proc near ;;;;;;;;;;; push bx ;Already on stack push ds push cx push si push di push bp mov dx, cseg mov ds, dx mov ah, 0bh ;Read pots 0, 1, and 3. call ReadPots mov ax, si lea bx, Pot0 call Normalize mov cl, al mov ax, bp lea bx, Pot1 call Normalize mov ch, al mov ax, di lea bx, Pot3 call Normalize mov dh, al ;Pot 3 value. mov ax, cx ;Pots 0 and 1. mov dl, 0 ;Pot 2 is non-existant. pop bp pop di pop si pop cx pop ds pop bx iret Read4Pots endp ;---------------------------------------------------------------------------- ; CalPot- Calibrate the pot specified by DL. On entry, AL contains ; the minimum pot value (it better be less than 256!), BX ; contains the maximum pot value, and CX contains the centered ; pot value. assume ds:cseg CalPot proc near pop bx ;Retrieve maximum value push ds push si mov si, cseg mov ds, si ; Sanity check on parameters, sort them in ascending order: mov ah, 0 cmp bx, cx ja GoodMax xchg bx, cx GoodMax: cmp ax, cx jb GoodMin xchg ax, cx GoodMin: cmp cx, bx jb GoodCenter xchg cx, bx GoodCenter: ; Okay, figure out who were supposed to calibrate: lea si, Pot0 cmp dl, 1 jb DoCal lea si, Pot1 je DoCal cmp dl, 3 jne CalDone lea si, Pot3 DoCal: mov [si].Pot.min, ax mov [si].Pot.max, bx mov [si].Pot.center, cx mov [si].Pot.DidCal, 1 CalDone: pop si pop ds iret CalPot endp assume ds:nothing ;---------------------------------------------------------------------------- ; TestCal- Just checks to see if the pot specified by DL has already ; been calibrated. assume ds:cseg TestCal proc near ;;;;;;;; push bx ;Already on stack push ds mov bx, cseg mov ds, bx sub ax, ax ;Assume no calibration lea bx, Pot0 cmp dl, 1 jb GetCal lea bx, Pot1 je GetCal cmp dl, 3 jne BadCal lea bx, Pot3 GetCal: mov al, [bx].Pot.DidCal mov ah, 0 BadCal: pop ds pop bx iret TestCal endp assume ds:nothing ;---------------------------------------------------------------------------- ; ; ReadSw- Reads the switch whose switch number appears in DL. SwTable byte 11100000b, 11010000b, 01110000b, 10110000b byte 00000000b, 11000000b, 01000000b, 10000000b SwTableL byte 11100000b, 10110000b, 01110000b, 11010000b byte 00000000b, 11000000b, 01000000b, 10000000b ReadSw proc near ;;;;;;; push bx ;Already on stack mov bl, dl ;Save switch to read. mov bh, 0 mov dx, JoyPort in al, dx and al, 0f0h cmp cs:SwapButtons, 0 je DoLeft0 cmp al, cs:SwTableL[bx] jne NotDown jmp IsDown DoLeft0: cmp al, cs:SwTable[bx] jne NotDown IsDown: mov ax, 1 pop bx iret NotDown: sub ax, ax pop bx iret ReadSw endp ;---------------------------------------------------------------------------- ; ; Read16Sw- Reads all eight switches and returns their values in AX. Read16Sw proc near ;;;;;;;; push bx ;Already on stack mov ah, 0 ;Switches 8-15 are non-existant. mov dx, JoyPort in al, dx shr al, 4 mov bl, al mov bh, 0 cmp cs:SwapButtons, 0 je DoLeft1 mov al, cs:SwBitsL[bx] jmp R8Done DoLeft1: mov al, cs:SwBits[bx] R8Done: pop bx iret Read16Sw endp ;**************************************************************************** ; ; MyInt15- Patch for the BIOS INT 15 routine to control reading the ; joystick. MyInt15 proc far push bx cmp ah, 84h ;Joystick code? je DoJoystick OtherInt15: pop bx jmp cs:Int15Vect DoJoystick: mov bh, 0 mov bl, dh cmp bl, 80h jae VendorCalls cmp bx, JmpSize jae OtherInt15 shl bx, 1 jmp wp cs:jmptable[bx] jmptable word BIOS word ReadPot, Read4Pots, CalPot, TestCal word ReadRaw, OtherInt15, OtherInt15 word ReadSw, Read16Sw JmpSize = ($-jmptable)/2 ; Handle vendor specific calls here. VendorCalls: je RemoveDriver cmp bl, 81h je TestPresence pop bx jmp cs:Int15Vect ; TestPresence- Returns zero in AX and a pointer to the ID string in ES:BX TestPresence: pop bx ;Get old value off stack. sub ax, ax mov bx, cseg mov es, bx lea bx, IDString iret ; RemoveDriver-If there are no other drivers loaded after this one in ; memory, disconnect it and remove it from memory. RemoveDriver: push ds push es push ax push dx mov dx, cseg mov ds, dx ; See if we're the last routine patched into INT 15h mov ax, 3515h int 21h cmp bx, offset MyInt15 jne CantRemove mov bx, es cmp bx, wp seg MyInt15 jne CantRemove mov ax, PSP ;Free the memory we're in mov es, ax push es mov ax, es:[2ch] ;First, free env block. mov es, ax mov ah, 49h int 21h ; pop es ;Now free program space. mov ah, 49h int 21h lds dx, Int15Vect ;Restore previous int vect. mov ax, 2515h int 21h CantRemove: pop dx pop ax pop es pop ds pop bx iret MyInt15 endp cseg ends ; The following segment is tossed when this code goes resident. Initialize segment para public 'INIT' assume cs:Initialize, ds:cseg Main proc mov ax, cseg ;Get ptr to vars segment mov es, ax mov es:PSP, ds ;Save PSP value away mov ds, ax mov ax, zzzzzzseg mov es, ax mov cx, 100h meminit2 print byte "Standard Game Device Interface driver",cr,lf byte "CH Products Flightstick Pro",cr,lf byte "Written by Randall Hyde",cr,lf byte cr,lf byte "'FSPSGDI LEFT' swaps the left and right buttons for " byte "left handed players",cr,lf byte "'FSPSGDI REMOVE' removes the driver from memory" byte cr, lf, lf byte 0 mov ax, 1 argv ;If no parameters, empty str. stricmpl byte "LEFT",0 jne NoLEFT mov SwapButtons, 1 print byte "Left and right buttons swapped",cr,lf,0 jmp SwappedLeft NoLEFT: stricmpl byte "REMOVE",0 jne NoRmv mov dh, 81h mov ax, 84ffh int 15h ;See if we're already loaded. test ax, ax ;Get a zero back? jz Installed print byte "SGDI driver is not present in memory, REMOVE " byte "command ignored.",cr,lf,0 mov ax, 4c01h ;Exit to DOS. int 21h Installed: mov ax, 8400h mov dh, 80h ;Remove call int 15h mov ax, 8400h mov dh, 81h ;TestPresence call int 15h cmp ax, 0 je NotRemoved print byte "Successfully removed SGDI driver from memory." byte cr,lf,0 mov ax, 4c01h ;Exit to DOS. int 21h NotRemoved: print byte "SGDI driver is still present in memory.",cr,lf,0 mov ax, 4c01h ;Exit to DOS. int 21h NoRmv: ; Okay, Patch INT 15 and go TSR at this point. SwappedLeft: mov ax, 3515h int 21h mov wp Int15Vect, bx mov wp Int15Vect+2, es mov dx, cseg mov ds, dx mov dx, offset MyInt15 mov ax, 2515h int 21h mov dx, cseg mov ds, dx mov dx, seg Initialize sub dx, ds:psp add dx, 2 mov ax, 3100h ;Do TSR int 21h Main endp Initialize ends sseg segment para stack 'stack' word 128 dup (0) endstk word ? sseg ends zzzzzzseg segment para public 'zzzzzzseg' byte 16 dup (0) zzzzzzseg ends end Main