		page	,132
;------------------------------------------------------------------------------
; Module Name: READAXIS.ASM
;
; Assembly language routine for reading joystick axis position
;
; Created: Feb 17th, 1992
; Author: Peter Wan
;
; Copyright (c) 1992 Advanced Gravis Computer Technology Limited
;
; Public Functions:
;	get_loop_count
;	read_axis
; Public Data:
;	None
; General Description:
;	This module contains functions to initialize itself and return
;	joystick axis position.
;------------------------------------------------------------------------------

		dosseg
		.model	 medium

;------------------------------------------------------------------------------
; Port address
;------------------------------------------------------------------------------
GAMEPORT	equ	201h		;gameport address
TIMER0		equ	40h		;8253 counter timer channel 0 address
TIMER_CONTROL	equ	43h		;counter timer control register address

		.data
max_loop_count	dw	?		;loop count for 3 milliseconds

		.code

;------------------------------------------------------------------------------
; get_loop_count
;
; The purpose of this routine is to determine how many iteration of the
; following instruction loop can be accomplished in 3 milliseconds.  The number
; of iteration count will be different on computers running at different speed
; or computers with different processing unit.	In the read joystick axis
; routine, it uses a similar program loop to determine joystick axis position
; and the count results from this routine will be used to limit axis read time
; up to 3 millesecond maximum.	This routine must be called once at
; initialization time.
;
; Entry:
;	None
; Returns:
;	None
; Error Returns:
;	None
; Registers Preserved:
;	BX,SI,DI,BP,DS,ES
; Registers Destroyed:
;	AX,CX,DX
; Calls:
;	None
;------------------------------------------------------------------------------

		public	_get_loop_count
_get_loop_count proc
init_count	equ	<[bp-2]>
		push	bp
		mov	bp,sp
		sub	sp,2		;2 bytes of local variable
		mov	max_loop_count,0;use loop count of 16 to start with
		mov	dx,GAMEPORT	;gameport address
glc3:
		mov	ax,0ff00h
		add	max_loop_count,16
		mov	cx,max_loop_count
		cli			;system interrupt must be disabled
		out	TIMER_CONTROL,al;latch count in timer 0
		jmp	short $+2
		in	al,TIMER0	;read low byte of latched count
		mov	init_count,al	;save
		jmp	short $+2
		in	al,TIMER0	;read high byte
		mov	init_count+1,al ;save
		out	dx,al		;trigger gameport
		in	al,dx		;delay to allow gameport to switch on
glc4:					;the following loop exits only when CX
					;reaches zero
		in	al,dx
		or	al,ah
		loopnz	glc4
		mov	ax,0
		jnz	$+2
		out	TIMER_CONTROL,al;latch count in timer 0
		sti			;enable system interrupt
		jmp	short $+2
		in	al,TIMER0	;read low byte of latched count
		mov	ah,al
		jmp	short $+2
		in	al,TIMER0	;read high byte
		xchg	al,ah
		sub	ax,init_count	;subtract count latched before
					;entering loop by count just read
		neg	ax
		cmp	ax,7160 	;has it exceed 3 milliseconds?
		jl	glc3		;no, increment loop count by 16 and
					;repeat process
		mov	sp,bp
		pop	bp
		ret
_get_loop_count endp

;------------------------------------------------------------------------------
; _read_axis
;
; This routine will accept a number from 0 to 3 for returning joystick axis
; position for joystick A-X, A-Y, B-X, B-Y respectively.  After bit position
; of requested axis is determined, count reading in counter timer chip channel 0
; will be read and saved.  A write cycle to gameport address 201 hex will
; trigger and toggle all four axis lines from low to high state.  The duration
; of these individual line staying high is directly proportional to the
; position of joystick for the individual axis.  After the requested axis line
; has returned to the low state, count reading in counter timer chip will be
; read again. The real time count of the duration the requested axis has stayed
; high can be obtained from the difference of the two counts read.  Allowance
; for other axis to return to low state must also be provided before next read.
; Therefore, return to calling function can only be done at the end of the
; 3 milliseconds duration.  The real time count return to the callind function
; can have a value ranges from 1 to maximum of 7160 depending on the position
; of the joystick, value of capacitors used and threshold voltage set
; on the gamecard.  If the requested axis has not returned to the low state
; after 3 milliseconds, it is assumed that no joystick is connected to that
; axis and zero will be returned to the calling function.  During the
; 3 milliseconds duration, all interrupts except NMI will be disabled.
;
; Entry:
;	[bp+6] = Axis to be read
;		 0 = Joystick A - X axis
;		 1 = Joystick A - Y axis
;		 2 = Joystick B - X axis
;		 3 = Joystick B - Y axis
; Returns:
;	AX = Count ranges from 1 to maximun of 7160 corresponds to joystick
;	     position
; Error Returns:
;	AX = 0 if no joystick connected
; Registers Preserved:
;	BX,SI,DI,BP,DS,ES
; Registers Destroyed:
;	AX,CX,DX
; Calls:
;	None
;------------------------------------------------------------------------------

		public	_read_axis
_read_axis	proc
axis		equ	<[bp+6]>
init_count	equ	<[bp-2]>
		push	bp
		mov	bp,sp
		sub	sp,2		;2 bytes of local variable
		sub	ax,ax		;determine bit position of axis to be
		mov	ah,1		;read
		mov	cx,axis
		shl	ah,cl
		mov	cx,max_loop_count;use 3 milliseconds loop count obtian
					;at initailization
		mov	dx,GAMEPORT	;gameport address
		cli			;disable interrupt
		out	TIMER_CONTROL,al;latch count in timer 0
		jmp	short $+2
		in	al,TIMER0	;read low byte of latched count
		mov	init_count,al	;save
		jmp	short $+2
		in	al,TIMER0	;read high byte
		mov	init_count+1,al ;save
		out	dx,al		;trigger gameport
		in	al,dx		;delay to allow gameport to switch on
ra1:
		in	al,dx		;read gameport
		test	al,ah		;requested axis return to low state or
					;3 milliseconds passed?
		loopnz	ra1		;no
		mov	ax,0		;return 0 if 3 milliseconds passed and
		jnz	ra3		;requested axis not yet in low state
		out	TIMER_CONTROL,al;requested axis return to low state
					;latch count in timer 0
		jmp	short $+2
		in	al,TIMER0	;read low byte of latched count
		mov	ah,al
		jmp	short $+2
		in	al,TIMER0	;read high byte
		xchg	al,ah
		sub	ax,init_count	;obtain real time count of high state
		neg	ax		;duration
		jcxz	ra3		;requested axis return to low state in
					;exactly 3 milliseconds, all other axis
					;should also have returned to low state
					;by now
		push	ax		;even though the requested axis has
		mov	ah,0ffh 	;returned to the low state, other axis
ra2:					;may still be in the high state, we
		in	al,dx		;should wait till the end of the
		or	al,ah		;3 milliseconds duration to allow the
		loopnz	ra2		;other axis to return to high state
		pop	ax		;before returning to the calling
					;fucntion
ra3:
		sti			;enable system interrupt
		mov	sp,bp
		pop	bp
		ret
_read_axis	endp
		end
