		page    ,132
;------------------------------------------------------------------------------
; Module Name: JOYREAD.ASM
;
; Assembly language routine for reading joystick axis position
;
; Created: April 13, 1995
; Author:  Darren Schueller
;
; Copyright (c) 1995 Advanced Gravis Computer Technology Limited
;
; Public Functions:
;       joyread
;
; Public Data:
;       None
;
; General Description:
;       This module contains a function that will read the joystick
;       axis values, along with the button states. 
;------------------------------------------------------------------------------

		.model   small, c

;------------------------------------------------------------------------------
;        Function prototype declaration
;------------------------------------------------------------------------------
joyread PROTO C value_ptr:FAR PTR WORD, butt_ptr:FAR PTR BYTE, axis_mask:BYTE

		.code

;------------------------------------------------------------------------------
; Port address
;------------------------------------------------------------------------------
GAMEPORT        equ     201h            ;gameport address

;------------------------------------------------------------------------------
; joyread
;
;
; Entry: 
;       Parameter #1 - Axes Values pointer      
;       ------------
;        The function is passed a pointer to an array of four unsigned 
;       integers, which will hold all of the axis values.  The array
;       is laid out as follows:
;
;       value_ptr[0] = Joystick #1 - X-Axis
;       value_ptr[1] = Joystick #1 - Y-Axis
;       value_ptr[2] = Joystick #2 - X-Axis
;       value_ptr[3] = Joystick #2 - Y-Axis
;
;       The values returned will be different for every computer, so
;       a "Centering" routine must be called in the application, that 
;       saves the minimum and maximum values of each axis, so that the
;       application can determine the relative stick position.
;
;
;       Parameter #2 - Button values pointer
;       ------------
;
;       The second parameter is a pointer to a byte that will receive
;       the button status values.  Only the lower nibble of the byte
;       is significant, and it is laid out as follows:
;
;       Bit #0 - Joystick 1/Button 1 --------------
;       Bit #1 - Joystick 1/Button 2 -----------   |
;       Bit #2 - Joystick 2/Button 1 --------   |  |
;       Bit #3 - Joystick 2/Button 2 -----   |  |  |
;                                         |  |  |  |
;                                         |  |  |  |
;                                         |  |  |  |
;                             X  X  X  X  0  0  0  0
;
;
;       Parameter #3 - Axis Mask Byte
;       ------------
;
;       The third parameter is a byte containing a bit-mask that
;       tells the function which axes to check, and which to ignore.
;       Because the function checks all four axes, the performance
;       of the function will suffer if there is one or more axes
;       missing (no stick connected).  This is because the routine
;       has to wait for the timeout value to expire when an axes
;       does not respond.  Only the bottom nibble of this byte is
;       significant, and it is laid out as follows:
;
;       Bit #0 - Joystick 1/X-Axis --------------
;       Bit #1 - Joystick 1/Y-Axis -----------   |
;       Bit #2 - Joystick 2/X-Axis --------   |  |
;       Bit #3 - Joystick 2/Y-Axis -----   |  |  |
;                                       |  |  |  |
;                                       |  |  |  |
;                                       |  |  |  |
;                           X  X  X  X  0  0  0  0
;
;       Setting the respective bit for an axis to 1, tells the
;       function to check that axis.  If the bit is a zero, the
;       function will ignore that axis.
;
;
; Returns:
;
;       The function returns a byte value that contains bits set in
;       the lower nibble representing which axes are valid (present).
;       If the bit is a 1, the axis is valid and did not timeout.  A
;       0 means that axis timed out and thus is most likely not there.
;       Only the lower nibble is used:
;
;       Bit #0 - Joystick 1/X-Axis --------------
;       Bit #1 - Joystick 1/Y-Axis -----------   |
;       Bit #2 - Joystick 2/X-Axis --------   |  |
;       Bit #3 - Joystick 2/Y-Axis -----   |  |  |
;                                       |  |  |  |
;                                       |  |  |  |
;                                       |  |  |  |
;                           X  X  X  X  0  0  0  0
;
;       This value can be used to determine which axes to poll in
;       subsequent calls to the function.
;
;       The typical scenario for an application would be like this:
;       Initially in the application, the joyread function would be
;       called with 0x0f in the Axis Mask.  This would read all axes,
;       and the return value would contain bits showing which axes
;       are there.  This value would be saved, and then used as the
;       Axis Mask parameter in subsequent calls to the function (like
;       inside the main program loop...).  This would then avoid any
;       unnecessary time overhead in polling axes that aren't there.
;
; Error Returns:
;       
;       NONE
;
; Registers Preserved:
;
;       BP, SI, DI, SS, DS
;
; Registers Destroyed:
;
;       AX, BX, CX, DX
;
; Calls:
;
;       NONE
;------------------------------------------------------------------------------

joyread         PROC C  value_ptr:FAR PTR WORD, butt_ptr:FAR PTR BYTE, axis_mask:BYTE

		push    bp
		push    si
		push    di
		push    ss
		push    ds

		lds     di,value_ptr    ;get pointer to values

		mov     dx,GAMEPORT     ;load game port io address

		cli                     ;stop interrupts

		out     dx,al           ;start gameport timers

		mov     cx,0ffffh       ;max count for timeout

		mov     bl,0fh          ;start with all 4 pulses hi

dowait:
		in      al,dx           ;Wait for change
		and     al,bl           ;Get low nibble for pulses
		and     al,axis_mask    ;mask out unwanted axes
		cmp     al,bl           ;Any changes?
		loopz   dowait          ;go and wait if no change

		cmp     cx,0            ;see if cx is zero
		jz      short timout    ;Timeout..took too long
		xor     al,bl           ;Set bits with changes
		push    ax              ;save changes for later
		push    cx              ;save count for later
		dec     cx              ;decrement timeout
		xor     bl,al           ;Update line status's
		jnz     dowait          ;Not done yet, go back
		xor     dh,dh           ;Clear return code
		jmp     done            ;Okay, all lines low...

timout:
		in      al,dx           ;Get bit values
		mov     dh,al           ;move to dh for return                
		push    ax              ;Dump dummy values
		push    cx              ;  onto stack
done:
		sti                     ;turn on the ints
		mov     dl,4            ;Decode change nibbles
decode:
		pop     bx              ;Get count off stack
		sub     bx,0fffeh       ;Turn down count....
		neg     bx              ; into an up count
		pop     ax              ;Get first change info
		mov     cx,4            ;Check four bits
decod1:
		shr     al,1            ;next bit set (changed?)
		jnc     short decod2    ;
		mov     [di],bx         ;Store the count
		dec     dl              ;
decod2:
		inc     di              ;Move di to next value
		inc     di              ;
		loop    decod1
		sub     di,8            ;reset di to vector start
		or      dl,dl           ;has it been 4 values?
		jnz     decode          ;Nope, keep going
		push    dx              ;save return val on stack
		mov     dx,GAMEPORT     ;load game port io address
		lds     di,butt_ptr     ;Get pointer to button value
		in      al,dx           ;Done, get the buttons..
		xor     ah,ah           ;kill the high byte
                xor     al,0f0h         ;Be nice and flip the bits
		mov     cl,4            ;Be nice and move the button
		shr     ax,cl           ; bits into the low nibble
		mov     [di],al         ;save button values

		pop     ax              ;get return value off stack
		and     ax,00f00h       ;mask off garbage bits...
		xchg    al,ah           ;Be nice and flip things
		xor     ax,0fh          ; around...

		pop     ds
		pop     ss
		pop     di
		pop     si
		pop     bp

		ret

joyread         endp

		end
