Polling the Joystick

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 Polling Routine

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

Calling Poll to Retrieve Button and Position Information

The GetPos function in IBMJOY.C calls Poll to retrieve the joystick information:

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;

}

Reading Button-Press Information

At the beginning of the Poll function, the code retrieves the button-state information for the joystick. Poll retrieves the button state information by reading from 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 code fragment shows the beginning of the Poll routine:

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

.

Reading Joystick Position Information

The next part of the assembly routine prepares for the polling loop, where the driver calculates the coordinate values. In this section, Poll performs the following tasks:

Clears the interrupt flag.

Initializes the coordinate variables. It sets the position counter variables to a negative base value, since the first iteration of the polling loop occurs before the potentiometer timers are started. See the next section for more information on the first iteration of the polling loop.

Records the delta calibration value for the coordinates in general-purpose registers.

Establishes the bit masks used to check the resistive flags during the polling loop.

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-position 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-position 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-position 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-position 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

.

The Polling Loop

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 incrementing

jz poll_y_test ; 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 is cleared, 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 cleared?

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