This section describes the procedure that IBMJOY.C follows when retrieving the button states and coordinate values for an IBM-compatible joystick. The procedure differs depending on which joystick is being polled and whether a three-dimensional joystick is being polled.
The POLL.ASM assembly language file contains a single routine, Poll, that interrogates the joystick for button press and coordinate information. Poll sets four global variables, wXpos, wYpos, wZpos, and wButtons to the coordinate and button values. The Poll function either polls the x and y axes or polls the z axis; for a three-dimensional joystick, IBMJOY.C calls Poll twice.
The Poll function is defined as follows:
extern UINT PASCAL Poll(UINT id, int zOnly)
The id parameter identifies the joystick number. The zOnly parameter identifies whether the z axis is being polled and if so, which axis of the second joystick interface is acting as the z axis. The zOnly parameter has one of the following values:
Value | Description |
0 | Poll x and y axes |
-1 | Poll z axis using the x axis of the second joystick interface |
1 | Poll z axis using the y axis of the second joystick interface |
In the following example, the GetPos function calls the Poll function to retrieve information about the joystick:
UINT GetPos(UINT id, JOYINFO FAR * lpInfo)
{
if(id > 1)
return JOYERR_PARMS; // Error...bad parameter
lpInfo->wZpos = 0;
if(wcAxes == 3) // Poll 3-axis joystick?
{
if(id == 1) // If 3D, must be joystick 1
return JOYERR_UNPLUGGED;
else
{
if(!Poll(1, iZaxis)) // Poll z axis
return JOYERR_UNPLUGGED;
lpInfo->wZpos = wYpos;
if(!Poll(0, 0)) // Poll x and y axes
return JOYERR_UNPLUGGED;
}
}
else if(!Poll(id, 0)) // Poll 2-axis joystick
return JOYERR_UNPLUGGED;
lpInfo->wButtons = wButtons;
lpInfo->wXpos = wXpos;
lpInfo->wYpos = wYpos;
return JOYERR_NOERROR;
}
The beginning of the Poll function retrieves status information for the joystick buttons. Poll retrieves the button status information by reading address 0x201. (For a description of address 0x201, see the table in "IBM Game Control Adapter Interface," earlier in this chapter.) The button state information is in bits 7 through 4. Poll inverts the bits and stores the resulting value in the wButtons global variable.
The following example shows the portion of the Poll routine that retrieves the button status information :
cProc Poll <PUBLIC> <si, di>
parmW id
parmW zOnly
localW wClear
cBegin
mov dx,JOYPORT ; Set joystick port address
in al,dx ; Read the port
mov cl,4 ; Shift at least four bits
cmp id, 0 ; If second joystick, shift six bits
jz @f
add cl,2
@@:
shr al,cl ; Shift flags
and ax,3 ; Clear the other bits just in case
xor ax,3 ; Invert the bits (port gives reverse logic)
mov _wButtons,ax ; Save flags in global variable
.
. ; Sample continued in next section
.
The following example examines the section of Poll where the driver calculates the coordinate values. In this section, Poll performs the following tasks:
The following code fragment continues where the previous fragment ended:
pushf ; Push the flags register
cli ; No interrupts allowed!
mov wClear, 1 ; Set stabilization loop flag
Real_Poll:
mov dx,JOYPORT ; Set joystick port address
mov si,offset _JoyCal ; Point to first JOYCALIBRATE structure
mov bx,JOY1_XY_MASK ; Place bit mask in bx
cmp id,0 ; Are we polling joystick 1?
jz @f ; If so, use first structure
cmp zOnly, 0 ; Are we polling the z axis?
jnz @f ; If so, use first structure
; Polling the second joystick
add si,(size JOYCALIBRATE) ; Use second JOYCALIBRATE structure
mov bx,JOY2_XY_MASK ; and bit mask
@@:
; Note zOnly values:
; 0 if polling x and y
; -1 if z axis is on the second joystick's x potentiometer
; 1 if z axis is on the second joystick's y potentiometer
cmp zOnly, 0 ; Are we polling the z axis?
jz poll_setxy ; No
jl @f ; Yes, on y potentiometer
; Yes, on x potentiometer
mov ax,[si].jcal_wZbase ; Set x-pos. counter to z base value
neg ax ; Start off negative
cwd
mov _wXpos,ax
mov _wXpos_hi,dx ; used to check for roll-over error
mov cx,[si].jcal_wZdelta ; Establish z delta value
mov bx,JOY2_X_MASK ; Put bit mask into dx
jmp poll_timers ; Start the timers!
; Set up for z-axis poll, using joystick 2's y potentiometer
@@: mov ax,[si].jcal_wZbase ; Set y-pos. counter to z base value
neg ax
cwd
mov _wYpos,ax
mov _wYpos_Hi,dx ; used to check for roll-over error
mov di,[si].jcal_wZdelta ; Establish z delta value
mov bx,JOY2_Y_MASK ; Put bit mask into bx
jmp poll_timers ; Start the timers!
; Set up for x- and y-axis poll
poll_setxy:
mov ax,[si].jcal_wXbase ; Set x-pos. counter to x base value
neg ax
cwd
mov _wXpos,ax
mov _wXpos_hi,dx ; used to check for roll-over error
mov ax,[si].jcal_wYbase ; Set y-pos. counter to y base value
neg ax ; Start off negative
cwd
mov _wYpos,ax
mov _wYpos_Hi,dx ; used to check for roll-over error
mov cx,[si].jcal_wXdelta ; Establish x and y deltas
mov di,[si].jcal_wYdelta
.
. ; Sample continued in next section
.
After setting up the required registers and global variables, Poll starts the polling loop. The first iteration of the polling loop occurs before the OUT to address 0x201; this is to ensure that the joystick's monostable vibrators have stabilized since the last poll. After the first iteration, Poll starts the timers by outputting a zero byte to address 0x201.
Each iteration of the polling loop checks the resistive flags. If the flag for a given axis is still set, the timeout has not yet occurred. Poll adds the delta value for that axis to the appropriate position variable. When all the flags are clear, Poll exits the loop. The value returned by this process depends on CPU speed; a fast CPU will complete more polling loops during the timeout period and therefore return higher coordinate values. This is why the IBMJOY driver requires calibration.
An unplugged joystick continually returns high bits for the position values. If a position counter exceeds a certain value (hard-coded as 0x30000 in POLL.ASM), assume the joystick is unplugged. Because the position value is dependent on CPU speed, the use of a constant ceiling value can present a problem with a very fast CPU (for example, a 50-MHz i486).
The following code fragment continues where the previous fragment ended:
; Start the timers!
poll_timers:
mov dx,JOYPORT ; Set joystick port address
cmp wClear, 1 ; Dummy loop?
je poll_skipout
xor ax,ax
out dx,al
poll_skipout:
mov ah,bl ; bl contains x-position bit mask
or ah,bh ; bh contains y-position bit mask
poll_loop:
in al,dx ; Get position info from joystick port
and al,ah ; Only watch bits of interest
jz poll_done ; Counters cleared?
poll_x_test:
test al,bl ; If x bit is cleared, stop
jz poll_y_test ; incrementing the x counter.
add _wXpos,cx ; X still high, increment counter
adc _wXpos_Hi,0
cmp _wXpos_Hi,3 ; Check for overflow (unplugged)
jl poll_y_test
xor ax,ax ; Unplugged. Return 0 and exit loop
jz poll_sti
poll_y_test:
test al,bh ; If y bit clear, stop incrementing
jz poll_loop
add _wYpos,di ; Y still high, increment counter
adc _wYpos_Hi,0
cmp _wYpos_Hi,3 ; Check for overflow (unplugged)
jl poll_loop ; No overflow, loop again
xor ax,ax ; Unplugged (return 0 and exit loop)
jz poll_sti
poll_done:
dec wClear ; Loop again if this is the dummy poll
jnz poll_sti
jmp Real_Poll
poll_sti:
pop bx ; Pop flags
test bx,200h ; Was interrupt flag already clear?
jz poll_fix_x ; Yes; don't reset it
sti ; No; reset interrupt flag
poll_fix_x: ; Clean up final x position value
xor bx,bx
cmp _wXpos_Hi,bx ; Check overflow variable
jz poll_fix_y ; If wXpos_Hi == 0, wXpos is OK
jl @f ; If wXpos_Hi < 0, set wXpos = 0
dec bx ; If wXpos_Hi > 0, set wXpos = 65535
@@: mov _wXpos,bx
poll_fix_y: ; Clean up final y position value
xor bx,bx
cmp _wYpos_Hi,bx
jz poll_fix_done
jl @f
dec bx
@@: mov _wYpos,bx
poll_fix_done:
mov al,ah ; ax = 0 for unplugged joystick
xor ah,ah ; ax != 0 if good read
cEnd ; All done