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

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;
}

Reading Button-Press Information

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
    .

Reading Joystick Position Information

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
    . 

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