		Page	60,132
		Title	All-Pro Hardware Dependent Routines
		Subttl	Software Notes, Documentation and History
		Page
;				   AllPro
;			   All-pro Hardware Interface

;	Module ...................... ALLPRO
;	Title ....................... All-pro Hardware Interface
;	Developing Engineer ......... George Gruber
;	Company ..................... Logical Devices
;	Revision Level .............. v1.10
;	Date of Last Revision ....... 01-14-87
;	Last Revision by ............ STH

;	These routines are very dependent upon hardware and construction of C.
;	Please take note when making any changes!.

		Include Lmacros.h

		Include Diag2asm.equ

		Include Device.equ

		Subttl	Unitialized Data Area and Externals
		Page
		Assume	DS:dataseg

dataseg 	Segment

		Global	udi_analog_reg_:byte,1		 ;udi internal memory map
		Global	analog_id_:byte,1		 ;analog control id code
		Global	SaveUdiMemory:word,2		 ;Udi memory map block beginning

		Extrn	phys_pin_offset_:word
		Extrn	pin_driver_flags_:word
		Extrn	pin_base_address_:word
		Extrn	module_id_:byte

		Extrn	at_286_flag_:byte
		Extrn	UdiSegment_:word		  ;Udi memory map area

slew_up_count	Dw	?			;slew rate up/down timer count
program_count	Dw	?			;program timer count

dataseg 	Ends



Xfer		Macro
		Mov	byte ptr ES:[XferAddress],AL
		Endm



		Subttl	Unlock Udi Interface
		Page
;	Unlock universal interface activates an interface board.
;
;Enter: 	Nothing
;
;Call:		pump ()
;
;Return:	universal interface activated.

	procdef pump

		Mov	DX,UdiMemMapPort
		Mov	AL, byte ptr UdiSegment_ + 1	;get high byte of segment map
		Out	DX,AL			;select udi interface segment
		Mov	DX,UdiLockPort0
		Mov	AL,0
		Out	DX,AL			;make sure udi is locked out
		Mov	DX,UdiLockPort0
		Mov	AL,0AAH
		Out	DX,AL			;now unlock udi
		Mov	DX,UdiLockPort1
		Mov	AL,055H
		Out	DX,AL

		pret				;...done

	pend	pump

		Subttl	Lock out Udi Interface
		Page
;	Lock universal interface de-activates an interface board.

;Enter: 	Nothing
;
;Call:		unpump ()
;
;Return:	universal interface de-activated.

	procdef unpump

		Mov	DX,UdiLockPort0
		Mov	AL,0A5H
		Out	DX,AL			;lock out udi from cpu

		pret				;...done

	pend	unpump



		Subttl	Get analog id code
		Page
;Enter	nothing
;
;Call get_analog_id
;
;Return analog_id = id code read from analog control.

	procdef get_analog_id

		Push	ES
		Mov	AX,[UdiSegment_]
		Mov	ES,AX

		Mov	BX,AnalogStatus
		Mov	AL,byte ptr ES:[BX]
		And	AL,0FH			;retain lower four bits
		Mov	analog_id_,AL

		pop	ES
		pret

	pend	get_analog_id


		Subttl	Read a Pin
		Page
;	Reads a pin specified by read_pin_num
;	returns a word indicating whether pin
;	read was greater than or less than 2.5 volts.

;Enter: 	read_pin_num		1 to 40

;Call:		read_pin (read_pin_num)

;Return:	a word indicating the state of the pin.

	procdef read_pin, <<read_pin_num, word>>

		Push	ES
		Mov	AX,[UdiSegment_]
		Mov	ES,AX

		Mov	AX,read_pin_num 	;get pin number
		Add	AX,[phys_pin_offset_]	;add physical pin offset
		Mov	BX,AX			;get pin number
		Shl	BX,1			;form offset into pin driver table
		Mov	BX,pin_base_address_[BX] ;get address of pin
		Mov	AL,byte ptr ES:[BX]	;read data
		And	AX,01

		Pop	ES

		pret				;...done

	pend	read_pin




	procdef write_dac, <<dac, word>, <val, word>>

		Mov	BX,dac			;get dac address
		Mov	AX,val			;get dac value
		Push	ES			;write data to device address
		Mov	DX,DS:[UdiSegment_]
		Mov	ES,DX
		Mov	byte ptr ES:[BX],AL
		Mov	byte ptr ES:[308H],AL	 ;Xfer
		Pop	ES

		pret

	pend	write_dac


	procdef get_module_id, <>

	Push	ES
	Mov	AX,DS:[UdiSegment_]
	Mov	ES,AX
	Mov	BX,GRP5PINDRIVER
	Sub	AH,AH
	Mov	AL, byte ptr ES:[BX]
	Pop	ES
	Mov	byte ptr module_id_,AL

	pret

	pend	get_module_id



		Subttl	Write Data to Memory on UDI
		Page
;	Write universal interface data byte.  This is provided primarily for
;	the diagnostics so that the DACs could be written without any adjust-
;	ments being performed on the data.

;Enter: 	mem_address		= device address to write
;		mem_data		= data to write to device
;
;Call:		write_udi_mem (mem_address, mem_data)
;
;Return:	the data written to the specified device address.

	procdef udi_write_mem, <<mem_address, word>, <mem_data, byte>>

		Mov	AL,mem_data		;get voltage to write to DAC
		Mov	BX,mem_address		;get the device address

		Push	ES			;write data to device address

		Mov	DX,[UdiSegment_]
		Mov	ES,DX
		Mov	byte ptr ES:[BX],AL

		Pop	ES

		pret				;...done

	pend	udi_write_mem

		Subttl	Write Sequencer
		Page
;	Write Data to WRITE SEQUENCER.	The WRITE SEQUENCER will shift out
;	the data to the pin drivers. A status FLIP-FLOP is used to indicate
;	a busy condition.

;Enter: 	seq_address		= sequence address to write
;		seq_data		= data to write to pins
;
;Call:		udi_write_sequencer (seq_address, seq_data)
;
;Return:	state of result flip_flop
;		0 = error during verify
;		1 = verified ok

	procdef udi_write_sequencer, <<seq_address, word>, <seq_data, byte>>


		Push	ES			;write data to device address
		Mov	DX,[UdiSegment_]
		Mov	ES,DX

		Mov	AX,seq_address		;get seq start address
		Mov	byte ptr ES:[SeqCounter],AL
		Mov	AL,seq_data
		Mov	byte ptr ES:[SeqShiftReg],AL

seq_wt: 	Test	byte ptr ES:[SeqBusy],1
		Jnz	seq_wt			;wait for not busy

		Mov	AL,byte ptr ES:[SeqResult]
		And	AX,1

		Pop	ES

		pret				;...done

	pend	udi_write_sequencer

		Subttl	Read Data from Memory on UDI
		Page
;	Read universal interface data byte.  This is provided primarily for
;	the diagnostics so that the anything could be read without any adjust-
;	ments being performed on the address.

;Enter: 	mem_address		= device address to write
;
;Call:		udi_read_mem (mem_address, mem_data)
;
;Return:	the data read from the specified device address.

	procdef udi_read_mem, <<mem_addr, word>>

		Mov	BX,mem_addr		;get the device address

		Push	ES			;write data to device address

		Mov	DX,[UdiSegment_]
		Mov	ES,DX
		Mov	AL,byte ptr ES:[BX]
		Sub	AH,AH

		Pop	ES

		pret				;...done

	pend	udi_read_mem

		Subttl	Write Data to DAC on UDI
		Page
;	Write universal device DAC (voltage) register.	Converts voltage
;	(specified in .01 volt increments) to .001 volt increments and divides
;	that down by VoltageIncremnt to yield an 8 bit value to write to device
;	address.

;	DAC compensations are as follows:
;		Supply1Dac			4.1 + .2 volts
;		Supply2Dac			4.1 + .2  volts
;		HpControlDac			1.4 + .2  volts
;		MpControlDac			2.1 + .2  volts
;		LpControlDac			2.1 + .2  volts
;		PuControlDac			0  + .2 volts
;		SlewRateDac			0
;		ThreshHoldDac			0

;	If the voltage specified is zero, no adjusting is done.

;Enter: 	dac_address		= device address to write
;		device_voltage		= data to write to device

;Call:		write_udi_dac (dac_address, device_voltage)

;Return:	the voltage written to the specified DAC.

	procdef udi_write_dac, <<dac_address, word>, <device_voltage, word>>

		Mov	AX,device_voltage	;get voltage to write to DAC
		Mov	BX,dac_address		;get the device address
		Mov	DX,0

		Cmp	BX,SlewRateDac		;is device slew rate dac?
		Je	WriteDacVolts		;...yes, just set value to device

		Mov	BX,10			;convert .01 volt increments to .001
		Mul	BX
		Mov	BX,VoltageIncremnt	;convert .001 volt increments to range 0 - 255
		Div	BX
		Mov	BX,dac_address		;get the device address

		Cmp	AX,0			;zero volts?
		Je	WriteDacVolts		;...yes, don't adjust at 0

		Cmp	BX,ThreshHoldDac	;is device threshold dac?
		Je	WriteDacVolts		;...yes, just set value to device

		Mov	DX,SUPPLY1ADD
		Cmp	BX,Supply1Dac
		Je	AdjPowerVoltag

		Mov	DX,PULLUPADD

		Cmp	BX,PuControlDac
		Je	AdjPowerVoltag

		Mov	DX,08

AdjPowerVoltag: Add	AX,DX			;add in power offset
		Cmp	BX,Supply1Dac
		Jne	WriteDacVolts

		Mov	CX,8
		Mul	CX
		Mov	CX,10
		Div	CX			;Vadj supply adjustment

WriteDacVolts:	Push	ES			;write data to device address

WriteDacCont:	Mov	DX,[UdiSegment_]
		Mov	ES,DX
		Cmp	AH,0			;see if dac count exceeded 255
		Je	WriteDacCont1

		Mov	AL,255

WriteDacCont1:	Mov	byte ptr ES:[BX],AL

		Cmp	BX,PuControlDac
		Jne	WrDacEnd

		Mov	byte ptr ES:[XferAddress],AL	;Xfer
WrDacEnd:
		Pop	ES

		pret				;...done

	pend	udi_write_dac

		Subttl	Write Bit State on UDI
		Page
;	Write universal device bit.  Changes the bit(s) specified to a speci-
;	fied state on the AnalogControl register.

;Enter: 	bit_address		= bits to change in register
;		bit_state		= state bit(s) are to change to

;Call:		write_udi_bit (bit_address, bit_state)

;Return:	the data written to the specified device address.

	procdef udi_write_bit, <<bit_address, byte>, <bit_state, byte>>

		Mov	AH,bit_address		;get device address and mask
		Mov	BX,AnalogControl	;analog control register address
		Mov	AL,udi_analog_reg_	;get last value written
		Or	AL,AH			;set bit
		Cmp	bit_state,0		;is the bit to be a zero?
		Jne	UpdateUdiBit		;...no, write new data to map and udi

		Not	AH			;make bit mask
		And	AL,AH			;leave all bits but selected one

UpdateUdiBit:	Mov	udi_analog_reg_,AL	;write new data to analog reg

		Push	ES			;write data to device address

		Mov	DX,[UdiSegment_]
		Mov	ES,DX

		Mov	byte ptr ES:[BX],AL

		Pop	ES

		pret				;...done

	pend	udi_write_bit



		Subttl	Read current
		Page
;	Read the state of the over-current bit

;Enter: 	current DAC set to specified over-current

;Call:		udi_read_ovc ()

;Return:	0 if under, 1 if over.

	procdef udi_read_ovc

		Mov	BX,AnalogStatus 	;analog status register address

		Push	ES			;write data to device address

		Mov	DX,[UdiSegment_]
		Mov	ES,DX

		Mov	AL, byte ptr ES:[BX]
		Rol	AL,1
		And	AX,1

		Pop	ES

		pret				;...done

	pend	udi_read_ovc


		Subttl	Write Bit State on UDI Pin
		Page
;	Write universal device pin driver.  Changes the drivers for a speci-
;	fied pin changing only the drivers needed to be changed.  The argument
;	pin contains both the drivers to turn on (in upper 8 bits) and the pin
;	to be affected (lower 8 bits). If the pin argument = phys_pin_offset,
;	it means that no pin is to be written (just exit).

;	Exception processing occurs as follows:
;		If CutOverFlag is set and the LpDriverOn is already set,
;		nothing is changed.  If the same drivers are to be turned on
;		are already on, then nothing is changed.

;Enter: 	pin			= pin to affect
;		bit_state		= state of bit to write

;Call:		udi_write_pin (pin | bit_state)

;Return:	the drivers turn on for the specified pin.

	procdef udi_write_pin, <<pin, word>>

		Mov	AX,pin			;get pin number and pin drivers
		Mov	DH,AH			;save new state of pin drivers
		Xor	AH,AH

		Shl	AX,1			;...no, write to pin
		Mov	BX,AX
		Mov	AX,pin_driver_flags_[BX]   ;check if any pins to change

same_drivr_chk: Xor	AX,DX
		Jz	write_pin_exit				;...no, done

		Push	ES

		Mov	AX,[UdiSegment_]
		Mov	ES,AX

		Mov	pin_driver_flags_[BX],DX ;set pin driver states
		Mov	BX,pin_base_address_[BX] ;get base address for pin
		Add	BX,ConfOffset
		Mov	byte ptr ES:[BX],DH	;write data to device address

		Pop	ES

write_pin_exit: pret				;...done

	pend	udi_write_pin



		Subttl	Write Dac on UDI Pin
;Call:		write_pin_dac (pin, voltage)


	procdef write_pin_dac, <<pinnum, word>, <voltage, word>>

		Push	ES

		Mov	AX,[UdiSegment_]
		Mov	ES,AX

		Mov	AX,voltage

		Mov	BX,10			;convert .01 volt increments to .001
		Mul	BX
		Mov	BX,VoltageIncremnt	;convert .001 volt increments to range 0 - 255
		Div	BX
		Add	AX,PINDACADD

		Mov	BX,pinnum		;get pin number and pin drivers
		Shl	BX,1			;...no, write to pin
		Mov	BX,pin_base_address_[BX]
		Add	BX,DacOffset		;make pins dac address

		Mov	byte ptr ES:[BX],AL	;write data to device address
		Mov	byte ptr ES:[XferAddress],AL

		Pop	ES

		pret				;...done

	pend	write_pin_dac




		Subttl	Set Dac on UDI Pin
;Call:		set_pin_dac (pin, voltage)
;		same as write pin dac except that physical pin offset
;		is added to pin number

	procdef set_pin_dac, <<pinnumber, word>, <pinvoltage, word>>

		Push	ES

		Mov	AX,[UdiSegment_]
		Mov	ES,AX

		Mov	AX,pinvoltage

		Mov	BX,10			;convert .01 volt increments to .001
		Mul	BX
		Mov	BX,VoltageIncremnt	;convert .001 volt increments to range 0 - 255
		Div	BX
		Add	AX,PINDACADD

		Mov	BX,pinnumber		;get pin number and pin drivers
		add	BX,[phys_pin_offset_]	;add offset
		Shl	BX,1			;index table
		Mov	BX,pin_base_address_[BX]
		Add	BX,DacOffset		;make pins dac address

		Mov	byte ptr ES:[BX],AL	;write data to device address
		Mov	byte ptr ES:[XferAddress],AL

		Pop	ES

		pret				;...done

	pend	set_pin_dac


		Subttl	Set Pin Configurator
		Page

;	Set pin configurator bits.

;Enter: 	pin_no	  -   pin number
;		cnf_mask  -   pattern to be written to latch (in high byte of int)

;Call:		set_pin_cnf (pin_no, cnf_mask)

;Return:	value written out

	procdef set_pin_cnf, <<pin_no, word>, <cnf_mask, word>>

		Push	ES

		Mov	AX,[UdiSegment_]
		Mov	ES,AX

		Mov	DX,cnf_mask				;load mask to DX
		Mov	BX,pin_no				;load pin # to BX
		Add	BX,[phys_pin_offset_]			;add offset
		Shl	BX,1					;form base address
		Mov	pin_driver_flags_[BX],DX		;save new pattern
		Mov	BX,pin_base_address_[BX]		;get address of pin
		Add	BX,ConfOffset				;add offset to point to configurator address
		Mov	byte ptr ES:[BX],DH			;write out to latch

		Pop	ES

		pret				;...done

	pend	set_pin_cnf


;	Set pin capacitor.

;Enter: 	pin_no	  -   pin number
;		cap state - ON or OFF

;Call:		set_pin_cap (pnum, cap_state)

;Return:	value written out

	procdef set_pin_cap, <<pnum, word>, <cap_state, word>>

		Push	ES

		Mov	AX,[UdiSegment_]
		Mov	ES,AX

		Mov	DX,cap_state				;load mask to DX
		Mov	BX,pnum 				;load pin # to BX
		Add	BX,[phys_pin_offset_]			;add offset
		Shl	BX,1					;form base address
		Mov	BX,pin_base_address_[BX]		;get address of pin
		Add	BX,ConfOffset1				;add offset to point to configurator address
		Mov	byte ptr ES:[BX],DL			;write out to latch

		Pop	ES

		pret				;...done

	pend	set_pin_cap


		Subttl	Clock a Pin
		Page
;	Clocks a specified pin with respect to a specified logic level (i.e.
;	either it is a high true clock or a low true clock).  This assumes
;	that the pin has been initialized to an idle state with either the
;	ground driver or the pullup driver on for this pin.

;Enter: 	clock_pin		= pin to clock
;		clock_logic		= logic level of clock
;						= 0 -> is low true clock
;						= 1 -> is high true clock

;Call:		udi_clock_pin (clock_pin, clock_logic)

;Return:	nothing

	procdef udi_clock_pin, <<clock_pin, word>, <clock_logic, word>>

		Push	ES			;write data to device address

		Mov	AX,[UdiSegment_]
		Mov	ES,AX

		Mov	BX,clock_pin		;get pin number
		Shl	BX,1
		Mov	BX,pin_base_address_[BX] ;get the base address for the pin
		Add	BX,ConfOffset

		Cmp	clock_logic,0		;is it low true clock?
		Je	LowTrueClock		;...yes, handle low true clock

		Mov	AH,high (mask TtlLoDriverOn)
		Mov	AL,high (mask TtlHiDriverOn)
		Mov	ES:[BX],AL		;turn ttl high driver on
		Nop
		Nop
		Mov	ES:[BX],AH		;turn ttl low driver on
		Jmp	short ClockDone 	;...done

LowTrueClock:	Mov	AH,high (mask TtlHiDriverOn) ;save starting state
		Mov	AL,high (mask TtlLoDriverOn)
		Mov	ES:[BX],AL		;turn ttl high driver on
		Nop
		Nop
		Mov	ES:[BX],AH		;turn ttl low driver on

ClockDone:	Pop	ES

		pret				;...done

	pend	udi_clock_pin

		Subttl	Read Bit State from UDI
		Page
;	Reads the state of a specified pin.  This does a lookup through
;	pin_driver_table to get the address of the pin driver then the pin
;	number is used to form the AND mask.  The data is read for the pin
;	and then ANDed with the mask and AX is either 0 if the data was zero
;	or not zero if the data was one.

;	Note:	*** This uses an absolute offset from pin_driver_table	    ***
;		*** Make sure that this is changed if the table is changed! ***

;Enter: 	read_pin		= pin to read

;Call:		udi_read_bit (read_pin)

;Return:	state of the bit	= (either zero if clear or non-zero
;					if the bit was on)

	procdef udi_read_bit, <<read_pin, word>>

		Mov	BX,read_pin		;get pin number
		Mov	CX,BX
		Shl	BX,1
		Mov	BX,pin_base_address_[BX]
		Mov	AH,01H

		Push	ES			;read device data byte

		Mov	DX,[UdiSegment_]
		Mov	ES,DX
		Mov	AL,byte ptr ES:[BX]

		Pop	ES

		And	AL,AH			;return AX = 0 if bit cleared, AX <> 0 if bit set
		Mov	AH,0

		pret				;...done

	pend	udi_read_bit


		Subttl	Get PC time
		Page

;	Get time will return the current value of the PC's time of day

;	Call	get_time()
;	returns long time

	procdef get_time
		Xor	AH, AH
		Int	1AH
		Mov	AX, DX
		Mov	DX, CX
		pret
	pend	get_time

		Subttl	Sound a tone for duration
		Page
;	Sound will make a 'tone' for 'duration' PC dependent

;	Call	sound(tone,duration)

	procdef sound, <<tone,word>,<duration,word>>
    mov     ax,tone
    cmp     ax,2000
    jle     nxt
    mov     ax,2000
nxt:
    mov     bx,2000
    sub     bx,ax

    cmp     tone, 0
    jna     Done
    cmp     duration, 0
    jna     Done
    mov     al, 10110110b
    out     43h, al
    mov     ax, tone
    out     42h, al
    mov     al, ah
    out     42h, al

    in	    al, 61h
    mov     ah, al
    or	    al, 3
    out     61h, al

    mov     cx, duration

Wait1:
    mov     dx, 100h
Wait2:
    dec     dx
    jnz     Wait2

    loop    Wait1

    mov     al, ah
    out     61h, al

Done:
		pret
	pend	sound


		Subttl	Delay n mS
		Page

;	Delay n mS.  Will delay a minimum of n milli-seconds.  This does not
;	shut down any interupts to leave the system running properly.

;Enter: 	delay_ms_time		= delay time in mS

;Call:		delay_ms (delay_ms_time)

;Return:	nothing 		= (time elapse minimum of n mS)

	procdef delay_ms, <<delay_ms_time, word>>

		Mov	AX,delay_ms_time	;get number of mS to delay
		Cwd
		Mov	BX,1200
		Mul	BX
		Mov	CX,AX
		Inc	DX
		Mov	CX,AX

DelayMsLoop:	Call	timer_delay		;delay timer 2 ticks

		Mov	CX,-1			;use full count from now on
		Dec	DX			;update mS to delay
		Jnz	DelayMsLoop		;...delay another mS

		pret				;...done

	pend	delay_ms

		Subttl	Delay n uS
		Page
;	Delay n mS.  Will delay a minimum of n micro-seconds.  This does not
;	shut down any interupts to leave the system running properly.

;Enter: 	delay_us_time		= delay time in uS

;Call:		delay_us (delay_us_time)

;Return:	nothing 		= (time elapse minimum of n uS)

	procdef delay_us, <<delay_us_time, word>>

		Mov	AX,delay_us_time	;get number of uS to delay
		Cwd
		Mov	BX,12
		Mul	BX
		Mov	BX,10
		Div	BX
		Mov	CX,AX
		Inc	DX
		Mov	CX,AX

		Call	timer_delay		;... delay timer 2 ticks

		pret				;...done

	pend	delay_us

		Subttl	Delay Thru Timer
		Page
;	Timer Delay.  Delays approximately CX timer 2 counts.  Pass the timer
;	2 ticks (derived by take uS * 12 and devide by 10) in CX.  If there is
;	less than 40 clocks (34 uS) for a PC type computer, no setup and wait
;	is done (provides approximately 28 uS).

;Enter: 	CX			= timer ticks to delay

;Call:		Mov	CX,(us * 12) / 10
;		Call	timer_delay

;Return:	nothing 		= (time elapse minimum of n uS)

		internal timer_delay

		Cmp	[at_286_flag_],0	;is it AT type computer?
		Jne	AtTimerDelay		;...yes, do AT timer delay

		Cmp	CX,40			;less than 34 uS?
		Jb	PcTimerDone		;...yes, yeilds about 28 uS

		In	AL,PPI8255PortB 	;enable timer 2 gate
		Or	AL,001H
		Out	PPI8255PortB,AL
		Mov	AL,0B0H 		;set timer 2 to mode 0 (int on term count)
		Out	CTC8253Command,AL
		Mov	AL,CL			;set counter 2 to count
		Out	CTC8253Timer2,AL
		Mov	AL,CH
		Out	CTC8253Timer2,AL
		Mov	CH,020H 		;set test mask into a register

Timer8253Wait:	In	AL,PPI8255PortC 	;read 8255 port C
		Test	AL,CH			;timer 2 interupt set?
		Jz	Timer8253Wait		;...no, continue to wait

PcTimerDone:	Ret				;...done

AtTimerDelay:	In	AL,PPI8255PortB
		Test	[at_286_flag_],0	   ;insert memory read cycle
		Test	[at_286_flag_],0	   ;insert memory read cycle
		Or	AL,1
		Out	PPI8255PortB,AL
		Test	[at_286_flag_],0	   ;insert memory read cycle
		Test	[at_286_flag_],0	   ;insert memory read cycle
Timerretry:
		Mov	AL,0B0H 		;set timer 2 to mode 0 (int on term count)
		Out	CTC8253Command,AL
		Test	[at_286_flag_],0	   ;insert memory read cycle
		Mov	AL,CL			;set counter 2 to count
		Out	CTC8253Timer2,AL
		Test	[at_286_flag_],0	   ;insert memory read cycle
		Mov	AL,CH
		Out	CTC8253Timer2,AL
		Test	[at_286_flag_],0	   ;insert memory read cycle
		Mov	CL,0E8H 		;read status command
		Mov	CH,080H 		;set test mask into a register

Timer8254Wait:	Mov	AL,CL			;issue read status command
		Out	CTC8253Command,AL
		Test	[at_286_flag_],0	   ;insert memory read cycle
		In	AL,CTC8253Timer2	;read the status back
		test	AL,40h			;check for 386 hang-up
		jnz	Timerretry		;attempt to recover
		Test	AL,CH			;timer 2 interupt set?
		Jz	Timer8254Wait		;...no, continue to wait

		Ret				;...done

timer_delay	endp

		finish

		End

