
	page 	65,132
	title	DRIVER.ASM Multi I/O Board Driver for Flashlite 386Ex
	subttl	Copyright JK microsystems 1998

;************************************************************************;
;*								       	*;
;*	Copyright JK microsystems 1998	-  All Rights Reserved	  	*;
;*									*;
;*	This software is NOT shareware, freeware, or public domain.	*;
;*	It is the property of JK microsystems.				*;
;*									*;
;*	Customers of JK microsystems may modify the source code		*; 
;*	and/or distribute the binary image of this software without 	*;
;*	additional costs provided it is run only on hardware 		*;
;*	manufactured by JK microsystems.  All other use is expressly 	*;
;*	prohibited.							*;
;*									*;
;*	Update Log							*;
;*									*;
;*	Version	Date	 Comments			 Progammer	*;
;*	-------	-------	 ------------------------------- ----------	*;
;*	1.0	10-20-98 First release	 		 jds		*;
;*	1.1     04-01-99 Fixed A/D chan 0 command byte	 jds		*;
;*	2.0	04-07-99 Fixed A/D for Rev C boards	 jds		*;
;*									*;
;************************************************************************;
	
	.model small
	.code

;-------------------------------------------------------------------------
;
;	The following routines are the C and Quickbasic entry points for
; 	the actual routines.  C entry points are prefaced with an under-
;	score.  At the entry point, the arguments are moved around so that 
;	a common routine can be called.  Also, a new stack is set up before 
;	entry and the old stack is restored on exit.
;
;	For information on the routines, see the _asm routine that is 
;	called.
;
public 		_GetVersion		
_GetVersion 	proc			; C routine
	push	bp
	mov 	bp,sp
	mov	ax,[bp+4]
	call	new_stack
	call	GetVersion_asm
	call	old_stack
	pop	bp
	ret
_GetVersion	endp

public 		GetVersion		; Qbasic routine
GetVersion	proc
	push	bp
	mov 	bp,sp
	mov	bx,ss:[bp+6]
	mov	ax,[bx]
	call	new_stack
	call	GetVersion_asm
	call	old_stack
	mov	[bx],ax
	pop	bp
	retf	2
GetVersion	endp

public 		_GetA2D		
_GetA2D 	proc			; C routine
	push	bp
	mov 	bp,sp
	mov	ax,[bp+4]
	call	new_stack
	call	GetA2D_asm
	call	old_stack
	pop	bp
	ret
_GetA2D		endp

public 		GetA2D			; Qbasic routine
GetA2D	 	proc
	push	bp
	mov 	bp,sp
	mov	bx,ss:[bp+6]
	mov	ax,[bx]
	call	new_stack
	call	GetA2D_asm
	call	old_stack
	mov	[bx],ax
	pop	bp
	retf	2
GetA2D		endp

public 		_PutA2DChannel
_PutA2DChannel 	proc			; C routine
	push	bp
	mov 	bp,sp
	mov	ax,[bp+4]
	call	new_stack
	call	PutA2DChannel_asm
	call	old_stack
	pop	bp
	ret
_PutA2DChannel	endp

public 		PutA2DChannel		; Qbasic routine
PutA2DChannel 	proc
	push	bp
	mov 	bp,sp
	mov	bx,ss:[bp+6]
	mov	ax,[bx]
	call	new_stack
	call	PutA2DChannel_asm
	call	old_stack
	mov	[bx],ax
	pop	bp
	retf	2
PutA2DChannel	endp

public 		_PutD2A		
_PutD2A 	proc			; C routine
	push	bp
	mov 	bp,sp
	mov	ax,[bp+4]
	call	new_stack
	call	PutD2A_asm
	call	old_stack
	pop	bp
	ret
_PutD2A		endp

public 		PutD2A			; Qbasic routine
PutD2A 		proc
	push	bp
	mov 	bp,sp
	mov	bx,ss:[bp+6]
	mov	ax,[bx]
	call	new_stack
	call	PutD2A_asm
	call	old_stack
	mov	[bx],ax
	pop	bp
	retf	2
PutD2A		endp

public 		_PutD2AChannel
_PutD2AChannel 	proc			; C routine
	push	bp
	mov 	bp,sp
	mov	ax,[bp+4]
	call	new_stack
	call	PutD2AChannel_asm
	call	old_stack
	pop	bp
	ret
_PutD2AChannel	endp

public 		PutD2AChannel		; Qbasic routine
PutD2AChannel 	proc
	push	bp
	mov 	bp,sp
	mov	bx,ss:[bp+6]
	mov	ax,[bx]
	call	new_stack
	call	PutD2AChannel_asm
	call	old_stack
	mov	[bx],ax
	pop	bp
	retf	2
PutD2AChannel	endp

public 		_PutDriver		
_PutDriver 	proc			; C routine
	push	bp
	mov 	bp,sp
	mov	ax,[bp+4]
	call	new_stack
	call	PutDriver_asm
	call	old_stack
	pop	bp
	ret
_PutDriver	endp

public 		PutDriver		; Qbasic routine
PutDriver 	proc
	push	bp
	mov 	bp,sp
	mov	bx,ss:[bp+6]
	mov	ax,[bx]
	call	new_stack
	call	PutDriver_asm
	call	old_stack
	mov	[bx],ax
	pop	bp
	retf	2
PutDriver	endp

public 		_PutDriverChannel
_PutDriverChannel 	proc		; C routine
	push	bp
	mov 	bp,sp
	mov	ax,[bp+4]
	call	new_stack
	call	PutDriverChannel_asm
	call	old_stack
	pop	bp
	ret
_PutDriverChannel	endp

public 		PutDriverChannel	; Qbasic routine
PutDriverChannel 	proc
	push	bp
	mov 	bp,sp
	mov	bx,ss:[bp+6]
	mov	ax,[bx]
	call	new_stack
	call	PutDriverChannel_asm
	call	old_stack
	mov	[bx],ax
	pop	bp
	retf	2
PutDriverChannel	endp

public 		_GetUARTChar		
_GetUARTChar 	proc			; C routine
	push	bp
	mov 	bp,sp
	mov	ax,[bp+4]
	call	new_stack
	call	GetUARTChar_asm
	call	old_stack
	pop	bp
	ret
_GetUARTChar	endp

public 		GetUARTChar		; Qbasic routine
GetUARTChar	proc
	push	bp
	mov 	bp,sp
	mov	bx,ss:[bp+6]
	mov	ax,[bx]
	call	new_stack
	call	GetUARTChar_asm
	call	old_stack
	mov	[bx],ax
	pop	bp
	retf	2
GetUARTChar	endp

public 			_PutUARTChannel		
_PutUARTChannel 	proc		; C routine
	push	bp
	mov 	bp,sp
	mov	ax,[bp+4]
	call	new_stack
	call	PutUARTChannel_asm
	call	old_stack
	pop	bp
	ret
_PutUARTChannel		endp

public 		PutUARTChannel		; Qbasic routine
PutUARTChannel	proc
	push	bp
	mov 	bp,sp
	mov	bx,ss:[bp+6]
	mov	ax,[bx]
	call	new_stack
	call	PutUARTChannel_asm
	call	old_stack
	mov	[bx],ax
	pop	bp
	retf	2
PutUARTChannel	endp

public 		_PutUARTChar		
_PutUARTChar 	proc			; C routine
	push	bp
	mov 	bp,sp
	mov	ax,[bp+4]
	call	new_stack
	call	PutUARTChar_asm
	call	old_stack
	pop	bp
	ret
_PutUARTChar	endp

public 		PutUARTChar		; Qbasic routine
PutUARTChar	proc
	push	bp
	mov 	bp,sp
	mov	bx,ss:[bp+6]
	mov	ax,[bx]
	call	new_stack
	call	PutUARTChar_asm
	call	old_stack
	mov	[bx],ax
	pop	bp
	retf	2
PutUARTChar	endp

public 		_PutUARTBaud		
_PutUARTBaud 	proc			; C routine
	push	bp
	mov 	bp,sp
	mov	ax,[bp+4]
	call	new_stack
	call	PutUARTBaud_asm
	call	old_stack
	pop	bp
	ret
_PutUARTBaud	endp

public 		PutUARTBaud		; Qbasic routine
PutUARTBaud	proc
	push	bp
	mov 	bp,sp
	mov	bx,ss:[bp+6]
	mov	ax,[bx]
	call	new_stack
	call	PutUARTBaud_asm
	call	old_stack
	mov	[bx],ax
	pop	bp
	retf	2
PutUARTBaud	endp

public 		_GetUARTRxStatus		
_GetUARTRxStatus 	proc			; C routine
	push	bp
	mov 	bp,sp
	mov	ax,[bp+4]
	call	new_stack
	call	GetUARTRxStatus_asm
	call	old_stack
	pop	bp
	ret
_GetUARTRxStatus	endp

public 		GetUARTRxStatus		; Qbasic routine
GetUARTRxStatus	proc
	push	bp
	mov 	bp,sp
	mov	bx,ss:[bp+6]
	mov	ax,[bx]
	call	new_stack
	call	GetUARTRxStatus_asm
	call	old_stack
	mov	[bx],ax
	pop	bp
	retf	2
GetUARTRxStatus	endp

public 		_GetUARTTxStatus		
_GetUARTTxStatus 	proc			; C routine
	push	bp
	mov 	bp,sp
	mov	ax,[bp+4]
	call	new_stack
	call	GetUARTTxStatus_asm
	call	old_stack
	pop	bp
	ret
_GetUARTTxStatus	endp

public 		GetUARTTxStatus		; Qbasic routine
GetUARTTxStatus	proc
	push	bp
	mov 	bp,sp
	mov	bx,ss:[bp+6]
	mov	ax,[bx]
	call	new_stack
	call	GetUARTTxStatus_asm
	call	old_stack
	mov	[bx],ax
	pop	bp
	retf	2
GetUARTTxStatus	endp

public 		_PutUARTWC		
_PutUARTWC 	proc			; C routine
	push	bp
	mov 	bp,sp
	mov	ax,[bp+4]
	call	new_stack
	call	PutUARTWC_asm
	call	old_stack
	pop	bp
	ret
_PutUARTWC	endp

public 		PutUARTWC		; Qbasic routine
PutUARTWC	proc
	push	bp
	mov 	bp,sp
	mov	bx,ss:[bp+6]
	mov	ax,[bx]
	call	new_stack
	call	PutUARTWC_asm
	call	old_stack
	mov	[bx],ax
	pop	bp
	retf	2
PutUARTWC	endp

public 		_PutUARTWD		
_PutUARTWD 	proc			; C routine
	push	bp
	mov 	bp,sp
	mov	ax,[bp+4]
	call	new_stack
	call	PutUARTWD_asm
	call	old_stack
	pop	bp
	ret
_PutUARTWD	endp

public 		PutUARTWD		; Qbasic routine
PutUARTWD	proc
	push	bp
	mov 	bp,sp
	mov	bx,ss:[bp+6]
	mov	ax,[bx]
	call	new_stack
	call	PutUARTWD_asm
	call	old_stack
	mov	[bx],ax
	pop	bp
	retf	2
PutUARTWD	endp

public 		_GetUARTRC		
_GetUARTRC 	proc			; C routine
	push	bp
	mov 	bp,sp
	mov	ax,[bp+4]
	call	new_stack
	call	GetUARTRC_asm
	call	old_stack
	pop	bp
	ret
_GetUARTRC	endp

public 		GetUARTRC		; Qbasic routine
GetUARTRC	proc
	push	bp
	mov 	bp,sp
	mov	bx,ss:[bp+6]
	mov	ax,[bx]
	call	new_stack
	call	GetUARTRC_asm
	call	old_stack
	mov	[bx],ax
	pop	bp
	retf	2
GetUARTRC	endp

public 		_GetUARTRD		
_GetUARTRD 	proc			; C routine
	push	bp
	mov 	bp,sp
	mov	ax,[bp+4]
	call	new_stack
	call	GetUARTRD_asm
	call	old_stack
	pop	bp
	ret
_GetUARTRD	endp

public 		GetUARTRD		; Qbasic routine
GetUARTRD	proc
	push	bp
	mov 	bp,sp
	mov	bx,ss:[bp+6]
	mov	ax,[bx]
	call	new_stack
	call	GetUARTRD_asm
	call	old_stack
	mov	[bx],ax
	pop	bp
	retf	2
GetUARTRD	endp

;-------------------------------------------------------------------------
;
;	Data structures
;
VersionNo	dw	10		; driver version number

ad_board	db	0		; last A2D channel settings
ad_side		db	0
ad_chan		db	0

da_board	db	0		; last D2A channel settings
da_side		db	0
da_chip		db	3
da_bit		db	0

driver_board	db	0		; last driver channel settings
driver_side	db	0
driver_chip	db	5
driver_bit	db	0
driver_bits	db	16 dup(0)	; status of all the driver bits
driver_bit_ptr	dw	offset driver_bits

uart_board	db	0		; last uart channel settings
uart_side	db	0
uart_chip	db	2

uart_cmd	dw	16 dup(0E00Bh)	; default 9600,N82

uart_cmd_ptr	dw	offset uart_cmd

;	Read A/D channel #0 single-ended command string

RD_0:	
	db	50h,70h			; 1 Start
	db	40h,60h			; 0 A2
	db	40h,60h			; 0 A1
	db	50h,70h			; 1 A0
	db	40h,60h			; 0 Mode
	db	50h,70h			; 1 Single - Diff/
	db	50h,70h			; 1 PD1
	db	50h,70h			; 1 PD0
	db	40h,60h			; one extra clock
	db	40h			; idle the bus, data is high 
	db	0			; null terminator

;	Read A/D channel #1 single-ended

RD_1:	
	db	50h,70h			; 1 Start
	db	50h,70h			; 1 A2
	db	40h,60h			; 0 A1
	db	50h,70h			; 1 A0
	db	40h,60h			; 0 Mode
	db	50h,70h			; 1 Single - Diff/
	db	50h,70h			; 1 PD1
	db	50h,70h			; 1 PD0
	db	40h,60h			; one extra clock
	db	40h			; idle the bus, data is high 
	db	0			; null terminator

;	Read A/D channel #2 single-ended

RD_2:	
	db	50h,70h			; 1 Start
	db	40h,60h			; 0 A2
	db	50h,70h			; 1 A1
	db	40h,60h			; 0 A0
	db	40h,60h			; 0 Mode
	db	50h,70h			; 1 Single - Diff/
	db	50h,70h			; 1 PD1
	db	50h,70h			; 1 PD0
	db	40h,60h			; one extra clock
	db	40h			; idle the bus, data is high 
	db	0			; null terminator

;	Read A/D channel #3 single-ended

RD_3:	
	db	50h,70h			; 1 Start
	db	50h,70h			; 1 A2
	db	50h,70h			; 1 A1
	db	40h,60h			; 0 A0
	db	40h,60h			; 0 Mode
	db	50h,70h			; 1 Single - Diff/
	db	50h,70h			; 1 PD1
	db	50h,70h			; 1 PD0
	db	40h,60h			; one extra clock
	db	40h			; idle the bus, data is high 
	db	0			; null terminator

baud_table:				
	dw	230	,0		; 230.4k baud
	dw	115	,1		; 115.2k baud
	dw	57	,2		; 57.6k baud
	dw	38	,9		; 38.4k baud
	dw	19	,10		; 19.2k baud
	dw	9600	,11		; 9600 baud
	dw	4800	,12		; 4800 baud
	dw	2400	,13		; 2400 baud
	dw	1200	,14		; 1200 baud
	dw	600	,15		; 600 baud
	dw	0	,0		; list end

select_table:
S0	db	70h,50h,60h,40h,60h,40h,60h,40h
S1	db	70h,50h,60h,40h,60h,40h,70h,50h
S2	db	70h,50h,60h,40h,70h,50h,60h,40h
S3	db	70h,50h,60h,40h,70h,50h,70h,50h
S4	db	70h,50h,70h,50h,60h,40h,60h,40h
S5	db	70h,50h,70h,50h,60h,40h,70h,50h
S6	db	70h,50h,70h,50h,70h,50h,60h,40h
S7	db	70h,50h,70h,50h,70h,50h,70h,50h

;-------------------------------------------------------------------------
;
;	GetVersion_asm - Returns the driver version number.  The least
;	significant decimal digit is the minor revision, the most
;	significant decimal digits are the major revision.
;
GetVersion_asm:
	mov	ax,[VersionNo]
	ret

;-------------------------------------------------------------------------
;
;	GetA2DChannel_asm - Do one A/D conversion and return 12 bit 
;	value right justified in AX
;
GetA2D_asm:
	mov	al,byte ptr [ad_chan]
	mov 	bh,byte ptr [ad_board]
	mov	bl,byte ptr [ad_side]

	push	bx

	mov	bl,20			; calculate pointer to channel data
	mul	bl
	mov	ah,0
	mov	si,offset RD_0
	add	si,ax

	pop	bx
	push	bx

	mov	dx,0F862h		; point to output port
	mov	al,40h			; drop all but cs/
	out	dx,al	

	mov	al,0			; set CS0/ for write
	call	select

	mov	dx,0F864h		; configure all Port F pins for
	mov	al,0			; output
	out	dx,al

	mov	dx,0F862h		; point to output port
cmd_loop:
	mov	al,cs:[si]		; get a byte
	cmp	al,0			; done?
	je	cmd_done		; yes, leave
	out	dx,al			; no, send it
	inc	si			; bump pointer
	jmp	cmd_loop		; and loop

cmd_done:
	mov	dx,0F864h		; configure data line for read
	mov	al,10h
	out	dx,al

	mov	cx,16			; 16 bits to get
	mov	bx,0			; result goes here
	mov	ah,0
in_loop:
	mov	dx,0F860h
	in	al,dx			; data is in bit 4

	rcl	al,1			; rotate it into carry
	rcl	al,1
	rcl	al,1
	rcl	al,1
	rcl	bx,1			; rotate it from carry to bx

	mov	dx,0F862h		; point to output port
	mov	al,70h			; set the clock high
	out	dx,al

	mov	al,50h
	out	dx,al

	loop	in_loop	
	
	shr	bx,1			; last 3 bits are zeros, shift 
	shr	bx,1			; 12 bit value over
	shr	bx,1
	shr	bx,1
	and	bx,0FFFh

	mov	dx,0F862h		; idle the bus
	mov	al,40h
	out	dx,al
	mov	ax,bx

	call	bus_reset

	pop	bx

	sti
	ret


;-------------------------------------------------------------------------
;
;	PutA2DChannel_asm gets the extended channel from the caller and
;	converts it to board, side, and channel numbers and stores
;	the result.
;
PutA2DChannel_asm:
	push	ax
	mov	ah,al			; make 2 copies
	mov	bl,al
	and	al,3			; channel
	mov	byte ptr [ad_chan],al	; store it
	and	ah,4			; board
	shr	ah,1
	shr 	ah,1
	mov	byte ptr [ad_side],ah	; store it
	and	bl,0F8h
	shr	bl,1
	shr	bl,1
	shr	bl,1
	mov	byte ptr [ad_board],bl
	pop	ax
	ret


;-------------------------------------------------------------------------
;
;	PutD2AChannel_asm converts AL into a chip, side, and board select
;	and stores them for PutD2A_asm.
;
PutD2AChannel_asm:
	push	ax
	push	bx
	and	al,1Fh			; 8 boards max
	mov	bh,al			; make 2 copies
	mov 	bl,al
	and	al,1			; al is chip select
	add	al,3

	shr	bl,1			; bl is side					
	and	bl,1
	
	shr	bh,1			; bh is board
	shr	bh,1
	and	bh,7			; 8 boards max

	mov	byte ptr [da_chip],al
	mov	byte ptr [da_side],bl
	mov	byte ptr [da_board],bh

	pop	bx
	pop	ax
	ret


;-------------------------------------------------------------------------
;
;	PutD2A_asm sends the lower 12 bits in ax to the selected D2A
;
PutD2A_asm:
	push	ax
	push	ax

	mov	al,byte ptr [da_chip]
	mov	bl,byte ptr [da_side]
	mov	bh,byte ptr [da_board]

	call	select	

	pop	bx
	and	bx,0FFFh

	mov	al,40h			; set data low
	mov	dx,0F862h
	out	dx,al

	mov	dx,0F864h		; configure all pins as outputs
	mov	al,0			; 
	out	dx,al

	mov	cl,16			; loop counter
	rcl	bx,1			; get the first data bit in carry
	mov	dx,0F862h		; output port

da_shift_loop:
	mov	al,60h			; clk hi with data zero
	jnc	da_data_low		; all done if carry is 0
	mov	al,70h			; clk hi with data one
da_data_low:
	out	dx,al			; do it
	and	al,0D0h			; lower clk
	out	dx,al			; send it
	
	rcl	bx,1			; rotate
	dec	cl
	jnz	da_shift_loop

	call	bus_reset

	pop	ax
	ret


;-------------------------------------------------------------------------
;
;	PutDriverChannel_asm takes an integer in the range of 0-63 in 
;	ax and converts it into a board number, side number and an index
;	into the array of previous driver bit states.
;
PutDriverChannel_asm:
	push	ax			; save ax for caller

	and	al,3fh			; only look at lower 6 bits
	mov	bh,al			; make 3 copies
	mov	bl,al
	mov	ah,al

	shr	bl,1			; form the side
	shr	bl,1
	and	bl,1
	mov	[driver_side],bl				

	shr	bh,1			; form the board
	shr	bh,1
	shr	bh,1
	and	bh,7
	mov	[driver_board],bh

	and	ah,3
	mov	[driver_bit],ah

	mov	ah,0
	shr	al,1			; form the pointer to the bitfield
	shr	al,1
	and	al,0Fh
	mov	si,offset driver_bits	; point to array
	add	si,ax			; index into it 
	mov	[driver_bit_ptr],si	; store the pointer	

	pop	ax
	ret

;-------------------------------------------------------------------------
;
;	PutDriver_asm sets or cleared the relay driver channel selected
;	by PutDriver_Channel.  It has to pull up the previous 4 bits of
;	the channel group, set or clear the specified channel bit, store 
;	the updated nibble and shift it out to the drivers.
;
PutDriver_asm:
	push	ax

	cmp	al,0			; convert al to one or zero
	je	zero

	mov	al,00000001B		; OR mask for one
	mov	ah,11111111B		; AND mask for one
	jmp	one

zero:
	mov	al,00000000B		; OR mask for zero
	mov	ah,11111110B		; AND mask for zero
one:
	mov	cl,[driver_bit]		; get bit position
bit_shift:
	cmp	cl,0			; done?
	je	done_shifting
	rol	al,1
	rol	ah,1
	dec	cl
	jmp	bit_shift		; now our bit is in the same position
					; as in the nibble we will shift out
done_shifting:
	mov	si,[driver_bit_ptr]	; get pointer to our nibble
	mov	bl,[si]			; get our nibble
	or	bl,al			; do the OR
	and	bl,ah			; do the AND
	mov	[si],bl			; store the updated nibble

	push	bx

	mov	bh,[driver_board]	; select the 4 bit group
	mov	bl,[driver_side]	; from the channel specified
	mov	al,[driver_chip]	; by PutDriverChannel
	call	select

	pop	bx

	shl	bl,1			; left justify the data
	shl	bl,1
	shl	bl,1
	shl	bl,1
	and	bl,0F0h			; get rid of any garbage

	mov	al,40h			; set data low
	mov	dx,0F862h
	out	dx,al

	mov	dx,0F864h		; configure all pins as outputs
	mov	al,0			; 
	out	dx,al

	mov	cl,5			; loop counter
	rcl	bl,1			; get the first data bit in carry
	mov	dx,0F862h		; output port

shift_loop:
	mov	al,60h			; clk hi with data zero
	jnc	data_low		; all done if carry is 0
	mov	al,70h			; clk hi with data one
data_low:
	out	dx,al			; do it
	and	al,0D0h			; lower clk
	out	dx,al			; send it
	
	rcl	bl,1			; rotate
	dec	cl
	jnz	shift_loop

	call	bus_reset

	pop	ax			; restore caller's value
	ret

;-------------------------------------------------------------------------
;	
;	PutUARTChannel_asm -  Selects the current UART, 0-15
;
PutUARTChannel_asm:
	push	ax			; save a copy for caller
	mov	ah,al			; and another copy for here
	and	al,00001111B		; 16 max

	mov	bl,al			; form side number
	and	al,00000001B
	mov	[uart_side],al

	mov	bh,ah			; form board number
	shr	bh,1
	and	bh,00000111B
	mov	[uart_board],bh

	pop	ax			; set pointer to write config word
	push	ax

	mov	si,offset uart_cmd
	shl	ax,1
	and	ax,01Eh			; 16 entries max
	add	si,ax
	mov	[uart_cmd_ptr],si
	pop	ax
	ret

;-------------------------------------------------------------------------
;	
;	PutUARTChar_asm - Sends the char in al out the current UART - blocking
;
PutUARTChar_asm:
	push	ax
	call	send_char_wait
	pop	ax
	ret

;-------------------------------------------------------------------------
;	
;	PutUARTBaud_asm - Sets the baud rate for the current UART
;
PutUARTBaud_asm:
	push	ax
	mov	si,offset baud_table	; point to the baud rate translation
baud_loop:				; table
	cmp	ax,[si]			; get entry
	je	found_baud		; match?
	cmp	[si],word ptr 0		; no, last entry?
	je	baud_not_found		; yes, all done
	add	si,4			; no, index to next entry
	jmp	baud_loop		; and back through

found_baud:				; we have a match
	add	si,2			; point to uart baud code
	mov	bx,[si]			; get it
		
	mov	si,[uart_cmd_ptr]	; point to the previous uart command
	mov	ax,[si]			; get it
	and	ax,0FFF0h		; clear the baud bits
	or	ax,bx			; OR in the selected baud
	mov	[si],ax			; store it
	call	set_uart		; and send it
	
baud_not_found:
	pop	ax
	ret

;-------------------------------------------------------------------------
;
;	GetUARTRxStatus - Get Rx status bit in AX, 1 equals char waiting
;
GetUARTRxStatus_asm:
	call	GetUARTRC_asm
	rol	ax,1
	and	ax,1
	ret

;-------------------------------------------------------------------------
;
;	GetUARTTxStatus - Get TX status bit in AX, 1 equals ok to send
;
GetUARTTxStatus_asm:
	call	GetUARTRC_asm
	rol	ax,1
	rol	ax,1
	and	ax,1
	ret

;-------------------------------------------------------------------------
;
;	GetUARTChar_asm - Gets a char from the current UART - blocking
;
GetUARTChar_asm:
	call	get_char_status		; char available?
	jnc	GetUARTChar_asm		; no, loop checking
	call	get_char		; yes, get it
	mov	ah,0
	ret

;-------------------------------------------------------------------------
;
;	PutUARTWC_asm - Writes AX to the write configuration reg of the
;	current UART
;
PutUARTWC_asm:
	mov	bx,ax
	or	bl,11000000b		; make sure top 2 bits are set
	mov	[uart_cmd_ptr],bx	; store it for next time

	call	uart
	mov	ax,bx
	ret

;-------------------------------------------------------------------------
;
;	PutUARTWD_asm - Writes AX to the Write Data reg of the current UART
;
PutUARTWD_asm:
	mov	bx,ax
	or	bl,10000000b		; make sure bit 15 is set
	and	bl,10111111b		; and bit 14 is cleared
	call	uart
	mov	ax,bx
	ret

;-------------------------------------------------------------------------
;
;	GetUARTRC_asm - Returns the read configuration register in AX from
;	the current UART
;
GetUARTRC_asm:
	mov	bx,4000h
	call	uart
	mov	ax,bx
	ret

;-------------------------------------------------------------------------
;
;	GetUARTRD_asm - Returns the read data register in AX from the 
;	current UART
;
GetUARTRD_asm:
	mov	bx,0
	call	uart
	mov	ax,bx
	ret

;-------------------------------------------------------------------------
;
;	Send_Char_Wait - Checks to see if it's ok to send a char, if it
;			 is, sends the char in AL.  If not, waits til ok
;			 then sends char in AL.
;
send_char_wait:
	call	send_char_status	; ok to send?
	jnc	send_char_wait		; no, loop checking
	call	send_char		; yes, send it
	ret

;-------------------------------------------------------------------------
;
;	Send_Char_Status - Returns with carry set if ok to send char
;
send_char_status:
	push	ax
	push	bx			; needed for uart command
	mov	bx,4000h		; read data register cmd
	call	uart			; do it
	shl	bh,1			; get the T bit
	shl	bh,1			; in carry
	pop	bx			; restore bx
	pop	ax
	ret

;-------------------------------------------------------------------------
;
;	Send_Char - Unconditionally sends char in AL
;
send_char:
	push	ax			; save AX
	mov	bl,al			; move the char to BL
	mov	bh,80h			; send command
	call	uart			; send it
	pop	ax
	ret

;-------------------------------------------------------------------------
;
;	Get_Char_Status	- Returns with carry flag set if char available
;
get_char_status:
	push	bx			; needed for uart command
	mov	bx,4000			; read data register cmd
	call	uart			; do it
	shl	bh,1			; get the R bit
	pop	bx			; restore bx
	ret

;-------------------------------------------------------------------------
;
;	Get_Char - Gets a char from UART without testing. Char returned
;		   in AL.
;
get_char:
	push	bx			; needed for uart command
	mov	bx,0			; read data register cmd
	call	uart			; do it
	mov	al,bl			; we have data, move to al
	pop	bx			; restore bx
	ret

;-------------------------------------------------------------------------
;
;	Set_UART - Sends AX to the UART write config reg
;
set_uart:
	push	ax			; data is passed to uart in bx
	mov	bx,ax
	call	uart
	pop	ax
	ret	

;-------------------------------------------------------------------------
;
;	Uart - Simultaneously shift in and out the 16 bit value in BX
;	
uart:
	push	ax
	push	cx
	push	dx
	push	bx

	mov	bh,byte ptr [uart_board] ; get board, side and chip from mem
	mov	bl,byte ptr [uart_side]
	mov	al,byte ptr [uart_chip]
	call	select

	mov	al,40h			; set data low
	mov	dx,0F862h
	out	dx,al

	mov	dx,0F864h		; configure data as open-drain
	mov	al,10h			; CLK, RESET, and CS as comp. out
	out	dx,al

	pop	bx
	mov	cl,16			; loop counter
	rcl	bx,1			; get the first data bit in carry

u_shift_loop:
	mov	al,40h			; low clock, data 0
	jnc	u_data_low		; all done if carry is 0
	mov	al,50h			; no, make data 1
u_data_low:
	mov	dx,0F862h		; drive it out
	out	dx,al
	mov	al,70h			; raise clk and data
	out	dx,al			; drive it out
	
	mov	dx,0F860h
	in	al,dx			; get our data
	rcl	al,1
	rcl	al,1
	rcl	al,1
	rcl	al,1

	rcl	bx,1			; rotate
	dec	cl
	jnz	u_shift_loop

	call	bus_reset

	pop	dx
	pop	cx
	pop	ax
	ret

;-------------------------------------------------------------------------
;
;	Select - Activates the chip select for the proper device
;		 BH = Board to select, 0 - 8
;		 BL = Side to select, 0 - 1
;		 AL = Chip Select, 0 - 5
;
select:
	call	bus_reset		; reset the bus to start
	push	ax			; save the chip select
	mov	cl,bh			; get board in cl
board_select_loop:
	cmp	cl,0			; any to do
	je	side_select		; no, do the side
	mov	al,7			; select next board
	call	bus_channel		; do it
	dec	cl			; decrement board count
	jmp	board_select_loop	; and loop
side_select:
	cmp	bl,0			; if zero, don't do anything
	je	chip_select
	mov	al,6			; select side 1
	call	bus_channel
chip_select:
	pop	ax			; get the chip select
	call	bus_channel		; and do it
	ret

;-------------------------------------------------------------------------
;
;	Reset - send a reset pulse to all of the decoders on the bus
;
bus_reset:
	push	ax
	push	dx

	mov	dx,0F862h		; put a zero in the output ports
	mov	al,0
	out	dx,al

	mov	dx,0F864h		; select all outputs
	out	dx,al

	mov	dx,0F862h
	mov	al,40h			; lift reset
	out	dx,al

	pop	dx
	pop	ax

	ret

;-------------------------------------------------------------------------
;
;	Select selects the channel passed in AL
;
bus_channel:
	push	bx
	push	si			; going to need some registers
	push	dx
	push	ax

	mov	bl,al			; channel needs to go into bl

	mov	bh,0
	shl	bl,1			; multiply bl times 8 for proper
	shl	bl,1			; table offset
	shl	bl,1

	mov	si,offset select_table	; point to first entry
	add	si,bx			; add in offset

	mov	bx,8			; loop counter now
	mov	dx,0F862h		; point to port

send_loop:
	mov	al,cs:[si]		; get the byte
	out	dx,al			; send it
	inc	si			; bump pointer
	dec	bx			; unbump counter
	jnz	send_loop		; done?

	mov	al,050h			; idle the bus
	out	dx,al

	pop	ax
	pop	dx
	pop	si
	pop	bx

	ret

;-----------------------------------------------------------------------------
;
;	New_stack sets up a new stack and saves all of the caller's 
;	registers except AX. DS and ES are set to the same code 
;	segment as CS.
;
new_stack:
	mov	cs:[old_ax],ax		; preserve ax
	mov	cs:[old_bx],bx		; and bx
	pop	bx			; get our return in bx
	cli				; interrupts off while changing stack
	mov	ax,ss			; get stack seg
	mov	cs:[old_ss],ax		; store it
	mov	cs:[old_sp],sp		; store old stack pointer
	mov	ax,cs			; get our code seg
	mov	ss,ax			; set our stack seg to it
	mov	sp,offset top_stack	; point to our stack
	sti

	push	cx			; save caller's registers
	push	dx
	push	si
	push	di
	push	bp
	push	ds
	push	es

	push	bx			; push caller's return adddress
	
	mov	ds,ax			; set small model ds and es
	mov	es,ax

	mov	ax,cs:[old_ax]		; restore ax
	mov	bx,cs:[old_bx]		; and bx
	
	ret				; and return to caller	

;-----------------------------------------------------------------------------
;
;	Old_stack restores the old stack and the caller's registers 
;	except AX.
;
old_stack:
	mov	cs:[old_ax],ax		; preserve ax
	pop	bx			; get our return in bx

	pop	es			; restore caller's registers
	pop	ds
	pop	bp		
	pop	di
	pop	si
	pop	dx
	pop	cx

	cli				; interrupts off while changing stack
	mov	sp,cs:[old_sp]		; get old stack pointer
	mov	ax,cs:[old_ss]		; get old stack seg
	mov	ss,ax			; install it
	sti
	push	bx			; push caller's return address
	mov	ax,cs:[old_ax]		; restore ax
	mov	bx,cs:[old_bx]		; and bx
	ret				; and return to caller	
	

old_ax	dw	0			; temp register storage for 
old_bx	dw	0			; new_stack and old_stack
old_sp	dw	0
old_ss	dw	0
	dw	200h	dup(0)		; new stack
top_stack:
		end

