home *** CD-ROM | disk | FTP | other *** search
- ;===========================================================================
- ;
- ; G A M E R - Assembly language joystick support for Turbo Pascal
- ;
- ;===========================================================================
- ;
- ; by Jeff Duntemann 12 February 1988
- ; with thanks to Ted Mirecki for additional insights
- ;
- ; From: COMPLETE TURBO PASCAL 5.0 by Jeff Duntemann
- ; Scott, Foresman & Co., Inc. 1988 ISBN 0-673-38355-5
- ;
- ;
- ; GAMER is a single assembly-language source file that contains both
- ; STICK.ASM and BUTTON.ASM, which are given separately elsewhere in
- ; COMPLETE TURBO PASCAL, 3E. The purpose of GAMER is to show how multiple
- ; assembly language procedures may be combined into a single machine-code
- ; module to lessen program clutter.
- ;
- ; The idea is to create a Turbo Pascal unit incorporating the routines in
- ; this module. The unit source file is GAMEBORD.PAS. The headers of both
- ; routines must be laid out in the interface section of GAMEBORD.PAS, and
- ; the .OBJ file containing the routines must be loaded into the
- ; implementation section of GAMEBORD.PAS using the $L compiler directive:
- ;
- ; INTERFACE
- ;
- ; FUNCTION Button(StickNumber,ButtonNumber : Integer) : Boolean;
- ;
- ; PROCEDURE Stick(StickNumber : Integer;
- ; VAR X : INTEGER;
- ; VAR Y : INTEGER);
- ;
- ; IMPLEMENTATION
- ;
- ; {$L GAMER}
- ; FUNCTION Button; EXTERNAL;
- ; PROCEDURE Stick; EXTERNAL;
- ;
- ;
- ;
- ; GAMEBORD.PAS is given elsewhere in COMPLETE TURBO PASCAL, 3E
- ;
- ;
- ; To reassemble/relink GAMER:
- ;-------------------------------------
- ; Assemble this file with MASM. "C>MASM GAMER;"
- ;
- ;
-
-
- CODE SEGMENT BYTE PUBLIC ; THE SEGMENT IS BYTE-ALIGNED
- ASSUME CS:CODE
- PUBLIC BUTTON,STICK ; THE TWO ACCESSIBLE PROCS IN THIS MODULE
-
-
-
- ;===========================================================================
- ; B U T T O N - Function to return the state of the joystick buttons
- ;===========================================================================
- ;
- ; The full function header follows:
- ;
- ; FUNCTION BUTTON(StickNumber,ButtonNumber : Integer) : Boolean;
- ;
- ; StickNumber specifies which joystick to read from, and ButtonNumber
- ; specifies which of the two buttons on that joystick to read. If the
- ; specified button is down, BUTTON returns a Boolean value of TRUE.
- ;
- ; Yes, this is the long way 'round; assembly language is in no way required
- ; to read four bits from an ordinary 8088 I/O port. BUTTON exists only as
- ; practice in creating assembly language external functions.
- ;
- ; The button information is obtained by reading I/O port $201. The high
- ; four bits represent the state of the four buttons (two for each of the
- ; two possible joysticks) at the instant the port is read. A LOW bit
- ; represents a button DOWN. This is why the byte read from the port is
- ; inverted via NOT before the selected bit is tested.
- ;
- ; Here is a map of the button bits as returned by port $201:
- ;
- ; |7 6 5 4 3 2 1 0|
- ; | | | |
- ; | | | - - - - - - -> Button #1, joystick #1
- ; | | - - - - - - - -> Button #2, joystick #1
- ; | - - - - - - - - -> Button #1, joystick #2
- ; - - - - - - - - - -> Button #2, joystick #2
- ;
- ; Remember that the return value from this function is passed to the runtime
- ; code in the AL register.
- ;
- ;
- ; This structure defines the layout of BUTTON's parameters on the stack:
- ;
- ONSTACK1 STRUC
- OLDBP DW ? ;TOP OF STACK
- RETADDR DD ? ;FAR RETURN ADDRESS
- BTN_NO DW ? ;BUTTON NUMBER
- STIK_NO DW ? ;STICK NUMBER
- ONSTACK1 ENDS
-
- BUTTON PROC FAR ;ALL PROCS IN A UNIT ARE FAR PROCS
- PUSH BP ;SAVE PREVIOUS VALUE OF BP ON STACK
- MOV BP,SP ;SP BECOMES NEW VALUE OF BP
-
- ;-------------------------------------------------------------------
- ; THE BULK OF THIS ROUTINE SETS UP A TEST MASK BY WHICH ONE SINGLE
- ; BIT OUT OF THE FOUR BUTTON BITS IS TESTED.
- ;-------------------------------------------------------------------
-
- MOV BL,010H ;START WITH HIGH BIT IN BIT 4
- CMP [BP].STIK_NO,2 ;ARE WE TESTING FOR JOYSTICK #2?
- JNE WHICH ;IF NOT, GO ON TO TEST FOR WHICH BUTTON,
- SHL BL,1 ; OTHERWISE SHIFT TWO POSITIONS LEFTWARD
- SHL BL,1 ; SO THAT THE MASK IS ON BIT 6 FOR STICK 2
-
- WHICH: CMP [BP].BTN_NO,2 ;ARE WE TESTING FOR BUTTON #2?
- JNE READEM ;IF NOT, MASK IS CORRECT; GO READ PORT
- SHL BL,1 ;OTHERWISE, SHIFT 1 BIT LEFT FOR BUTTON 2
-
- ;------------------------------------------------------------------------
- ; THE BIT MASK IS NOW CORRECT. HERE THE BUTTON BITS ARE READ FROM PORT
- ; $201 AND TESTED AGAINST THE MASK. NOTE THAT THE BITS AS READ FROM
- ; THE PORT MUST BE INVERTED SO THAT THE Z FLAG IS SET RATHER THAN CLEARED
- ; ON AN ACTIVE BUTTON BIT. (BITS ARE ACTIVE **LOW**, REMEMBER!)
- ;------------------------------------------------------------------------
-
- READEM: MOV DX,0201H ;SET UP 16-BIT ADDRESS FOR PORT READ
- IN AL,DX ;READ BUTTON BITS FROM PORT $201
- NOT AL ;MUST INVERT BITS FOR PROPER SENSE
- ; OF THE Z FLAG AFTER TESTING
- TEST AL,BL ;SEE IF THE DESIRED BIT IS HIGH;
- JNZ PUSHED ;IF SO, BUTTON IS PUSHED,
- MOV AL,0 ;SO MOVE BOOLEAN FALSE INTO AL
- JMP BDONE ;AND GET OUT OF HERE
-
- PUSHED: MOV AL,1 ;BUTTON DOWN; MOVE BOOLEAN TRUE INTO AL
-
- BDONE: MOV SP,BP ;RESTORE PRIOR STACK POINTER & BP
- POP BP ; IN CONVENTIONAL RETURN
- RET 6
-
- BUTTON ENDP
-
-
-
- ;===========================================================================
- ; S T I C K - Procedure to read either joystick
- ;===========================================================================
- ;
- ; The procedure header follows:
- ;
- ; PROCEDURE STICK(StickNumber : Integer VAR X,Y : Integer);
- ;
- ; StickNumber specifies which joystick to read from, and the X and Y
- ; parameters return integers proportional to the joystick's position
- ; at the moment the stick is sampled. These integers will vary from
- ; stick to stick depending on the resistance of the potentiometers
- ; used within the stick, but will typically from from 3 to 150.
- ;
- ; The IBM standard game controller board consists of two pairs of
- ; one-shots, which output a pulse when triggered by an I/O write to
- ; I/O port $201. The length of this pulse is determined by an RC
- ; time constant circuit the resistance portion of which is the
- ; potentiometer in the joystick. As the handle is moved around, the
- ; two potentiometers (one for X, one for Y) run up and back, changing
- ; resistance as they go.
- ;
- ; To read one of the two joysticks, a dummy value (which may be anything
- ; at all) is written to I/O port $201. Port $201 must then be polled
- ; continuously, incrementing a register at each polling event. When
- ; the bit corresponding to that stick's X or Y coordinate changes state,
- ; the count in the register is returned as that coordinate value at the
- ; time the stick was sampled.
- ;
- ; Here is a map of the joystick bits as returned by port $201:
- ;
- ; |7 6 5 4 3 2 1 0|
- ; | | | |
- ; | | | - - - - - - -> X coordinate, joystick #1
- ; | | - - - - - - - -> Y coordinate, joystick #1
- ; | - - - - - - - - -> X coordinate, joystick #2
- ; - - - - - - - - - -> Y coordinate, joystick #2
- ;
- ; One thing to keep in mind is that a bit goes LOW when sampled, and
- ; you must test for a HIGH on that bit to indicate that the one-shot has
- ; timed out.
- ;
- ;
- ;
- ; This structure defines STICK's parameters on the stack.
- ;
- ONSTACK2 STRUC
- OLDBP2 DW ? ;TOP OF STACK
- RETADDR2 DD ? ;FAR RETURN ADDRESS
- YADDR2 DD ? ;FAR ADDRESS OF X VALUE
- XADDR2 DD ? ;FAR ADDRESS OF Y VALUE
- STIK_NO2 DW ? ;STICK NUMBER
- ONSTACK2 ENDS
-
- ; EQUATES FOR ONE-SHOT BITS FOR STICKS 1 & 2
-
- STICK_X EQU 1
- STICK_Y EQU 2
-
-
- STICK PROC FAR
- PUSH BP ;SAVE CALLER'S BP
- MOV BP,SP ;STACK POINTER BECOMES NEW BP
- PUSH DS
-
- ; GET THE X AXIS VALUE FIRST
-
- MOV AH,STICK_X ; MOVE IN THE X TEST BIT
- CMP [BP].STIK_NO2,2 ; SEE IF WE'RE TESTING STICK #1 OR #2
- JNE TEST_X
- SHL AH,1 ; SHIFT BIT NUMBERS 2 LEFT FOR STICK #2
- SHL AH,1
- TEST_X: MOV AL,1 ; INITIALIZE OUTPUT VALUE
- MOV DX,201H ; SET PORT ADDRESS
- MOV BX,0 ; AND KEEPING THE RUNUP COUNT IN BX
- MOV CX,BX ; LOOP 64K TIMES MAX
- OUT DX,AL ; TRIGGER THE ONE-SHOTS
- AGAIN_X: IN AL,DX ; READ THE ONE-SHOT BITS
- TEST AL,AH ; TEST FOR A HIGH BIT 0
- JE DELAY ; WE'RE DONE IF BIT 0 IS HIGH
- INC BX ; OTHERWISE INCREMENT BX AND LOOP AGAIN
- LOOP AGAIN_X
- MOV BX,-1 ; SET X=-1 IF NO RESPONSE
-
- ; DELAY HERE TO LET THE OTHER THREE PULSES MAX OUT
-
- DELAY: MOV CX,512
- WAIT: LOOP WAIT
-
- ; NOW WE GET THE Y AXIS VALUE
-
- MOV AH,STICK_Y ; MOVE IN THE Y TEST BIT
- CMP [BP].STIK_NO2,2 ; SEE IF WE'RE TESTING STICK #1 OR #2
- JNE TEST_Y
- SHL AH,1 ; SHIFT BIT NUMBERS 2 LEFT FOR STICK #2
- SHL AH,1
-
- TEST_Y: MOV SI,0 ; KEEP THE RUNUP COUNT FOR Y IN SI
- MOV CX,SI ; SET LOOP LIMIT TO 64K
- OUT DX,AL ; FIRE THE ONE-SHOTS AGAIN
- AGAIN_Y: IN AL,DX ; READ THE ONE-SHOT BITS
- TEST AL,AH ; TEST FOR A HIGH BIT 1
- JE DONE ; WE'RE DONE IF BIT 1 IS HIGH
- INC SI ; OTHERWISE INCREMENT SI AND LOOP AGAIN
- LOOP AGAIN_Y
- MOV SI,-1 ; SET Y=-1 IF NO RESPONSE
-
- ; MOVE RETURN VALUES FROM REGISTERS INTO VAR PARAMETERS X & Y
-
- DONE: LDS DI,[BP].XADDR2 ;ADDR OF X INTO DS:DI
- MOV [DI],BX ;X VALUE FROM BX TO DS:DI
- LDS DI,[BP].YADDR2 ;DITTO FOR Y VALUE FROM SI
- MOV [DI],SI
-
- ; IT'S OVER...NOW CLEAN UP THE STACK AND LEAVE
-
- POP DS
- MOV SP,BP ; CLEAN UP STACK AND LEAVE
- POP BP ; RESTORE CALLER'S BP
-
- RET 10
-
- STICK ENDP
-
-
-
- CODE ENDS
- END