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