Metropoli BBS
VIEWER: express.asm MODE: TEXT (ASCII)
version	equ	4

	include	defs.asm
	include	8250defs.asm
;
;	Dave Price. October 30th 1990. 12:35.
;	Things much better now. I have had my standard
;	'FTP' test running at 2.5 Kbytes per second.
;	There is still a transmit bug though.. It appears
;	as the transmission of giant packets. I think I know
;	the source of the bug. I believe it occurs when we are
;	transmitting the last few bytes of a packet. Before
;	we change the interrupt mask to expect only TXDONE and
;	TXURUN, the TX fifo drains so that the TX419 condition
;	becomes true. The result is that the 8952 is already
;	waiting to interrupt us with the TX419. My code does not
;	expect anymore of these and acts incorrectly when
;	one arrives!
;
;	Dave Price. October 30th 1990 10:08.
;	After several minor changes the driver was almost
;	working. Some problems still existed however and
;	transferring data with FTP for instance
;	seemed to take a very long time. It appeared that
;	data was being lost, or that the routers or distant
;	host were so busy that packets were being dropped.
;	A further possible cause was that the timers used
;	by the TCP protocol engines were inappropriate or
;	in some sense incompatible. A carefull reading of
;	the data sheets implies that one can use the 'FIFO empty'
;	bits providing you make sure that at least 2 cycles
;	of a 2mHz clock occur after you read data and before you
;	read the interrupt flag register. This only amounts to
;	16 bus cycles on a 16Mhz PC. This is really very few
;	instructions. I have talked to Jeremy Bicknall at MITEL
;	and he seems to agree (I reported a potential design
;	bug with the 8952 generating false RX1519s - he will persue).
;	I have thus decided to use the RXBYTE bits to decide how
;	to process each item of data but use the RXFIFO empty state
;	to cease reading data.
;
;
;	Dave Price. October 24th 1990 11:10.
;	More changes again. The main idea now is 
;	to only have two states in the RX protocol engine.
;	It is either 'building' a frame or 'skipping'
;	to the next one. The actual interrupts will just
;	be used to indicate the point at which you should
;	stop processing the RXFIFO. There will be several
;	items to help. A minimum numbers of bytes to read,
;	a maximum number of bytes to read and a stop condition.
;	Processing the FIFO will cease when either the maximum
;	number of bytes have been processed, or BOTH the stop
;	condition and the minimum number have been processed.
;	On considering a new item of data a mask will be built
;	containing 5 bits that reflect a condition implied
;	by the data. Four bits are used to simply indicate
;	a packet byte, first byte, good last byte or bad last
;	byte. The fifth bit is used to indicate a frame abort
;	condition; this can only be determined by deciding
;	that the byte about to be read is a 'first' byte and
;	we already BUILDING a packet.
;	This change is a radical departure from previous
;	approaches to the RX code and might perhaps work
;	(HA, Ha!)
;
;	Dave Price. October 23rd 1990 16:06.
;	Some change of thought again.. I hate 8952s!
;	I am moving to four states in RX protocol engine.
;	'idle' will mean - finished one packet, awaiting next
;	'skipping' will mean we failed to get a buffer so
;	we are awaiting this packet to go by before trying
;	again for a buffer. I.E. we are discarding all input
;	waiting for an FA or EOPD etc etc
;	'found' means that the NEXT byte in the fifo
;	is a 'first' byte. I.E. here comes the packet...
;	'building' means we have a buffer and we are off
;	making up the next packet.
;
;	Dave Price. October 23rd 1990 09:40.
;	Having got completely fed up with lots of minor bugs
;	in the RX code, I am now carrying on with the changes
;	started earlier on 17th to attempt to have
;	some more clean code for the RX side. Most of the
;	code has been developed over the weekend but is 
;	handwriiten on the last listing. Problems
;	mainly arise with odd combinations of events
;	rather than simple circumstances. A major change is
;	that the RX code will now longer go and get itself
;	a buffer until the 'first byte' has been located. In
;	particular the completion of the collection of one
;	packet was immediately followed by the allocation
;	of a new buffer. This will now not happen.
;	I also intend at a later date to add fields to the
;	hdlc datastructure to hold port addresses etc. This
;	will start to pave the way for making the driver handle
;	multiple channels. It will require other changes as
;	well though (mainly stopping the code use constructions
;	like hdlc0.fred and instead move to set bx; [bx].fred.
;	This is not straightforward though as bs is already
;	used as a pointer. It will imply lots of pushing and
;	popping probably. All this is the next fix NOT
;	this change anyway.
;
;	October 17th 1990 20:30. Work starts to alter RX data
;	structures with a view to adding a 'state' variable
;	and dealing with input quite differently.
;
;	October 17th 1990.
;	Several new patches of code added to try to
;	the remaining bugs. Most bugs are caused by too long
;	packets being received (possibly because rx fails to
;	deal with FAs and RXOFLOWs correctly).
;
;	October 16th 1990. The code has now been used
;	fairly successfully. Some files have been transfered
;	using FTP from a sun via one NOS router over a 64Kbps
;	link from a second NOS PC. The central router had to
;	be rebooted once during the transfer as the driver
;	ran out of receive buffers! Amazingly the file
;	transfered o.k! The file was a 43Kbyte binary
;	of a virus checking program.
;	Code has been added to cope with RXofloe and Frame
;	abort, but bugs exist.
;
;	October 11th 1990.  The code has been running now
;	used by NOS. Some problems had occurred with 
;	events like txdone also having tx419 set.
;	Even though only txdone was enabled as an interrupt,
;	reading the 'interrupt flag register' showed both
;	bits set. As the code allows for several conditions
;	to be true it obeyed the one set of code and then 
;	attempted to handle the other condition too! This
;	resulted in errors.
;	The code now carefully processes txdone and then avoids
;	the tx419 condition!
;	Similar problems exist with eopd and fa!
;
;	October 1st 1990. Code is now in place to handle
;	rx and tx interrupts. user can also specify -n
;	so board acts as an NT.
;	Only RX1519, and EOPD handled on receive and TXDONE and
;	TX419 handles on transmit.
;
;	Buffer strategy Changed again. 25 September 1990. Dave Price
;	The idea now is that there will be a ring of
;	structures, each structure containing a little control
; 	information plus a Data Unit in which will be placed
;	an IP frame (or potentially any other type of frame).
;	There will be two such rings, one for transmission
;	and one for reception.
;	The rings will be statically allocated.
;	The Data Units will be set at 1500 bytes, the same
;	as the maximum MTU for ethernet packet drivers.
;
;	Simple byte-ring-buffer has proved awkward to
;	code. One often seems to be fighting the INTEL CPU.
;	On reflection a 'frame' based approach might be better.
;
;	Added Code for RJG suggested Buffer Management. The
;	idea is described in an ARUW?? document. There will
;	be a circular ring buffer of bytes (like the slip
;	drivers) with an associated structure to hold the
;	state of the buffers.
;
;	More bits from Dave Price to initialize
;	MITEL express card. Just plugs voice so far.
;	30/8/90
;
; This is a hacked version of slip8250 packet driver.
; The hack is beginning on 28/8/90.
; First attempts are just to change messages etc!
;
;	Changes started by Dave Price
;
;Ported from Phil Karn's asy.c and slip.c, a C-language driver for the IBM-PC
;8250 by Russell Nelson.  Any bugs are due to Russell Nelson.
;16550 support ruthlessly stolen from Phil Karn's 8250.c. Bugs by Denis DeLaRoca

;  Copyright, 1988-1992, Russell Nelson, Crynwr Software

;   This program is free software; you can redistribute it and/or modify
;   it under the terms of the GNU General Public License as published by
;   the Free Software Foundation, version 1.
;
;   This program is distributed in the hope that it will be useful,
;   but WITHOUT ANY WARRANTY; without even the implied warranty of
;   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;   GNU General Public License for more details.
;
;   You should have received a copy of the GNU General Public License
;   along with this program; if not, write to the Free Software
;   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

code	segment	byte public
	assume	cs:code, ds:code
;
;	Constants etc from MITEL Express card
;
;	First the DX (8980)
;
board_w_dx	dw	0300h

dx_b_con		equ	0000h
dx_b_cm_base	equ	0400h

dx_con_cmh		equ	00011000b
dx_con_cml		equ	00010000b
dx_cmh_mchan	equ	00000100b
dx_cmh_oe		equ	00000001b

;
;	Now the stream and channel assignments
;
snic_stream		equ	6
snic_d_channel	equ	0
snic_c_channel	equ	1
snic_b1_channel	equ	2
snic_b2_channel	equ	3

hdlc_stream		equ	6
hdlc_d_channel	equ	4
hdlc_c_channel	equ	5
hdlc_b1_channel	equ	6
hdlc_b2_channel	equ	7
hdlc_b3_channel	equ	8

dphone_stream		equ	7
dphone_unused_channel	equ	4
dphone_c_channel		equ	5
dphone_b1_channel		equ	6
dphone_b2_channel		equ	7
dphone_b3_channel		equ	8

;
;	Now some snic values etc
;
board_w_snic		dw	0b00h

snic_b_master		equ	0000h
snic_b_stbus		equ	0001h

snic_master_irqenable	equ	00000000b
snic_master_msdisable	equ	00000010b
snic_master_cstenable	equ	00000000b

snic_stbus_all	equ	0ffh

snic_c_ar		equ	10000000b
snic_c_dr		equ	01000000b
snic_c_dinb		equ	00100000b
snic_c_priority	equ	00010000b
snic_c_dreq		equ	00001000b
snic_c_txmch	equ	00000100b
snic_c_clrdia	equ	00000010b
snic_c_regsel	equ	00000001b

;
;	now some dphone values
;
board_w_dphone	dw	1700h

dphone_b_c		equ	0000h
dphone_b_time		equ	0005h
dphone_b_wdog		equ	0006h
dphone_b_tone1		equ	0007h
dphone_b_tone2		equ	0008h
dphone_b_dsp		equ	0009h
dphone_b_trans		equ	000ah
dphone_b_rgain		equ	000bh
dphone_b_sddata	equ	000ch
dphone_b_sddir		equ	000dh
dphone_b_test		equ	000eh

dphone_sddir_allout	equ	0ffh
dphone_sddata_te	equ	0b0h
dphone_sddata_nt	equ	0b8h

dphone_time_pcmb1	equ	00000001b
dphone_time_pcmb2	equ	00000100b
dphone_time_pcmb3	equ	00010000b

dphone_time_c		equ	10000000b

dphone_tone_697		equ	59h
dphone_tone_1209		equ	9bh

dphone_test_disable	equ	00h

dphone_dsp_cpcmen		equ	01000000b
dphone_dsp_dpcmen		equ	00100000b
dphone_dsp_dual		equ	00001000b
dphone_dsp_tone		equ	00010000b
dphone_dsp_speaker	equ	00011000b
dphone_dsp_cadence	equ	00000100b
dphone_dsp_warble16	equ	00000010b
dphone_dsp_dspen		equ	00000001b

dphone_trans_dial		equ	00100000b
dphone_trans_side		equ	00010000b
dphone_trans_hsmic	equ	00001000b
dphone_trans_spmic	equ	00000100b
dphone_trans_spskr	equ	00000010b
dphone_trans_hsskr	equ	00000001b

dphone_rgain_hpf		equ	10000000b
dphone_rgain_rfg_m7	equ	01110000b

;
;	now the hdlcs relative to the board base
;
board_w_hdlc0	dw	0f00h
board_w_hdlc1	dw	1300h
;
;	Now register offsets in the hdlc chips
;
hdlc_br_fifostatus	equ	00h
hdlc_br_receive		equ	01h
hdlc_bw_transmit		equ	01h
hdlc_b_control		equ	02h
hdlc_b_raddress		equ	03h
hdlc_b_cchancontrol	equ	04h
hdlc_b_time			equ	05h
hdlc_br_intflag		equ	06h
hdlc_bw_wdog		equ	06h
hdlc_bw_intenable		equ	07h
hdlc_br_genstatus		equ	08h
hdlc_br_cchanstatus	equ	09h

;
;	Now some values for the registers of the hdlc's
;
hdlc_fifostatus_RXBYTE		equ	11000000b
hdlc_fifostatus_packet		equ	00000000b
hdlc_fifostatus_first		equ	01000000b
hdlc_fifostatus_good		equ	10000000b
hdlc_fifostatus_bad		equ	01000000b
hdlc_fifostatus_last		equ	10000000b

hdlc_fifostatus_RXFIFO		equ	00110000b
hdlc_fifostatus_rxempty		equ	00000000b
hdlc_fifostatus_rxle14		equ	00010000b
hdlc_fifostatus_rxfull		equ	00100000b
hdlc_fifostatus_rxge15		equ	00010000b

hdlc_fifostatus_TXFIFO		equ	00001100b
hdlc_fifostatus_txfull		equ	00000000b
hdlc_fifostatus_txge5		equ	00000100b
hdlc_fifostatus_txempty		equ	00001000b
hdlc_fifostatus_txle4 		equ	00000100b

hdlc_control_txen		equ	10000000b
hdlc_control_rxen		equ	01000000b
hdlc_control_rxad		equ	00100000b
hdlc_control_ra6		equ	00010000b
hdlc_control_iftf1	equ	00001000b
hdlc_control_iftf0	equ	00000100b
hdlc_control_fa		equ	00000010b
hdlc_control_eop		equ	00000001b

hdlc_control_idle		equ	00000000b
hdlc_control_iftf		equ	00000100b
hdlc_control_trans	equ	00001000b
hdlc_control_goahead	equ	00001100b

hdlc_time_rst		equ	10000000b
hdlc_time_ic		equ	01000000b
hdlc_time_c1en		equ	00100000b
hdlc_time_brck		equ	00010000b
hdlc_time_tc		equ	00001111b

hdlc_time_c2bits8		equ	00000011b
hdlc_time_c3bits8		equ	00000100b
hdlc_time_c4bits8		equ	00000101b
hdlc_time_c23bits16		equ	00000110b
hdlc_time_c234bits24	equ	00000111b

hdlc_intflag_ga	equ	10000000b
hdlc_intflag_eopd	equ	01000000b
hdlc_intflag_txdone	equ	00100000b
hdlc_intflag_fa	equ	00010000b
hdlc_intflag_tx419	equ	00001000b
hdlc_intflag_txurun	equ	00000100b
hdlc_intflag_rx1519	equ	00000010b
hdlc_intflag_rxoflw	equ	00000001b

hdlc_intenable_ga		equ	10000000b
hdlc_intenable_eopd		equ	01000000b
hdlc_intenable_txdone	equ	00100000b
hdlc_intenable_fa		equ	00010000b
hdlc_intenable_tx419	equ	00001000b
hdlc_intenable_txurun	equ	00000100b
hdlc_intenable_rx1519	equ	00000010b
hdlc_intenable_rxoflw	equ	00000001b


hdlc_genstatus_rxoflw	equ	10000000b
hdlc_genstatus_txurun	equ	01000000b
hdlc_genstatus_ga	equ	00100000b
hdlc_genstatus_abrt	equ	00010000b
hdlc_genstatus_irq	equ	00001000b
hdlc_genstatus_idle	equ	00000100b


;
;	Now the overall interrupt register
;
board_w_intreg	dw	1b00h

intreg_hdlc0	equ	00000010b	;bit for hdlc0 interrupt
intreg_hdlc1	equ	00000001b	;bit for hdlc1 interrupt
intreg_snic	equ	00000100b	;bit for snic interrupt
intreg_hphone	equ	00001000b	;bit for d/hphone interrupt
;
;	now a few usefull macros
;
out_chip_reg_value	macro	chip,reg,value
	mov	dx,chip
	add	dx,reg
	mov	al,value
	out	dx,al
	endm

in_chip_reg		macro	chip,reg
	mov	dx,chip
	add	dx,reg
	in	al,dx
	endm

dx_message	macro	stream,channel,value
	out_chip_reg_value	board_w_dx,dx_b_con,<dx_con_cmh or stream>

	out_chip_reg_value	board_w_dx,<dx_b_cm_base or channel>,<dx_cmh_mchan or dx_cmh_oe>

	out_chip_reg_value	board_w_dx,dx_b_con,<dx_con_cml or stream>

	out_chip_reg_value	board_w_dx,<dx_b_cm_base or channel>,value

	endm

dx_source	macro	d_stream,d_channel,s_stream,s_channel

	out_chip_reg_value	board_w_dx,dx_b_con,<dx_con_cmh or d_stream>
	out_chip_reg_value	board_w_dx,<dx_b_cm_base or d_channel>,dx_cmh_oe
	out_chip_reg_value	board_w_dx,dx_b_con,<dx_con_cml or d_stream>
	out_chip_reg_value	board_w_dx,<dx_b_cm_base or d_channel>,<s_stream shl 5 or s_channel>

	endm

	public	int_no
int_no		db	7,0,0,0		; interrupt number.
NT_switch	db	0	;if 0 be a TE else be an NT

	public	driver_class, driver_type, driver_name, driver_function, parameter_list
driver_class	db	6,0,0,0		;from the packet spec
driver_type	db	0,0,0,0		;from the packet spec
driver_name	db	'EXPRESS',0	;name of the driver.
driver_function	db	2
parameter_list	label	byte
	db	1	;major rev of packet driver
	db	0	;minor rev of packet driver
	db	14	;length of parameter list
	db	EADDR_LEN	;length of MAC-layer address
	dw	GIANT	;MTU, including MAC headers
	dw	MAX_MULTICAST * EADDR_LEN	;buffer size of multicast addrs
	dw	0	;(# of back-to-back MTU rcvs) - 1
	dw	0	;(# of successive xmits) - 1
	dw	0	;Interrupt # to hook for post-EOI
			;processing, 0 == none,

;
;	Packet Buffer Structure

owner_k_empty	equ	0
owner_k_queue	equ	1
owner_k_isr	equ	2
info_k_size	equ	1500	;size of info area in buffer
;
buff		struc
buff_w_next	dw	0	;pointer to next buffer
buff_w_prev	dw	0	;pointer to previous buffer
buff_w_size	dw	0	;size of frame in info area in bytes
buff_w_owner	dw	owner_k_empty	;current owner of buffer
buff_info	db	info_k_size dup (0)	;area for the Transfer Unit
buff		ends
;
;	Structure for shared Queue Information
;	Now contains the info for the associated isr routines
;	as well. 17th October 1990.
;
;	First some constants.
;
state_k_skipping	equ	00000001b
state_k_building	equ	00000010b
upcall_k_idle		equ	0
upcall_k_active	equ	1
;
hdlc_data		struc
txq_w_front	dw	0	;pointer to front of tx queue
txq_w_back	dw	0	;pointer to back of tx queue
rxq_w_front	dw	0		;pointer to front of rx queue
rxq_w_back	dw	0		;pointer to back of rx queue
rxupcall_w_state	db	0	;state of any upcall
rxisr_w_state	db	0	;the current state of the isr
rxisr_w_pkt	dw	0	;pointer to the packet being used by rx ISR
rxisr_w_byte	dw	0	;pointer to the byte the rx ISR will use next	
rxisr_w_count	dw	0	;count of bytes inserted so far
txisr_w_pkt	dw	0	;pointer to the packet being used by tx ISR
txisr_w_byte	dw	0	;pointer to the byte tx ISR will use next
txisr_w_count	dw	0	;count of bytes remaining
copy_intflag	db	0	;copy of latest value from int flag
copy_intenable	db	0	;copy of latest value sent int enable
hdlc_data		ends

hdlc0_data	hdlc_data	<offset t1_buff, offset t1_buff, offset r1_buff, offset r1_buff,upcall_k_idle,state_k_skipping>			

;
;	Names for the bits in the byte_status_mask
;	rint_status_mask
;	and the stop_status_mask
;
mask_k_packet	equ	00000001b
mask_k_first	equ	00000010b
mask_k_good	equ	00000100b
mask_k_bad		equ	00001000b
mask_k_fabort	equ	00010000b

byte_status_mask	db	0	;used to save status implied
					;by the current bytes
;rint_status_mask	db	0	;used to save status implied
					;by the bytes so far in this
					;segment in the fifo
;stop_status_mask	db	0	;used to specify when we wish
					;	to stop
;
;	Locations to hold counters of bytes read in
;	one particular call of the interrupt code.
;

;number_read		db	0	;number read so far
;minimum_read	db	0	;minimum number that MUST be read
					; the interrupt style sets this
;maximum_read	db	0	;maximum available

	public	rcv_modes
rcv_modes	dw	4		;number	of receive modes in our table.
		dw	0,0,0,rcv_mode_3


	public	as_send_pkt
; The Asynchronous Transmit Packet routine.
; Enter with es:di -> i/o control block, ds:si -> packet, cx = packet length,
;   interrupts possibly enabled.
; Exit with nc if ok, or else cy if error, dh set to error number.
;   es:di and interrupt enable flag preserved on exit.
as_send_pkt:
	ret

	public	drop_pkt
; Drop a packet from the queue.
; Enter with es:di -> iocb.
drop_pkt:
	assume	ds:nothing
	ret

	public	xmit
; Process a transmit interrupt with the least possible latency to achieve
;   back-to-back packet transmissions.
; May only use ax and dx.
xmit:
	assume	ds:nothing
	ret


	public	send_pkt
;

send_pkt:
;enter with es:di->upcall routine, (0:0) if no upcall is desired.
;  (only if the high-performance bit is set in driver_function)
;enter with ds:si -> packet, cx = packet length.
;exit with nc if ok, or else cy if error, dh set to error number.
;called from telnet layer via software interrupt
	assume	ds:nothing

	push	cs
	pop	es
	assume	es:code
	sti				; enable interrupts
;
;	NOTE Using ES as segment for data accesses
;
	mov	bx,es:hdlc0_data.txq_w_back	;get pointer to back of queue
	cmp	es:[bx].buff_w_owner,owner_k_empty	;is it empty?
	jne	no_buffers_left		;no so error return...

	cmp	cx,info_k_size		;	check packet size o.k.
	jle	send_pkt_size_ok	; its fine
	pr_ch_al	'a'			;	error trace message
	mov	dh,CANT_SEND	;return an error code
	cli
	stc
	ret

send_pkt_size_ok:
	mov	es:[bx].buff_w_size,cx	;save size
	lea	di,es:[bx].buff_info		;
	rep	movsb				;and copy the packet

	mov	es:[bx].buff_w_owner,owner_k_queue	 ;give it to queue
	mov	bx,es:[bx].buff_w_next	;point to buffer
	mov	es:hdlc0_data.txq_w_back,bx		;adjust back of the queue
;
;	NOW WE NEED TO PROVOKE LOADING OF TXFIFO
;	IF WE THINK ISR GONE QUIET
;
; structure is at zero no ints active
	cli		;block interrupts starting before we exit
	cmp	es:hdlc0_data.txisr_w_pkt,0
	je	send_pkt_int_quiet	; jump if interrupts quiet
	clc					;clear carry because all o.k.
	ret
;
;	Else we now need to kick the interrupt code
;
send_pkt_int_quiet:
	push	ds		;save old ds and make it point to code
	push	cs
	pop	ds
	pr_ch_al	'b'
	call	tint_new		;manually call the tint routine !
	pop	ds			;restore old ds and
	ret				;return


no_buffers_left:
	pr_ch_al	'c'
	mov	dh,NO_SPACE
	cli	;block interrupts before we exit
	stc			;signal its an error - no more buffers
	ret

	public	get_address
get_address:
;get the address of the interface.
;enter with es:di -> place to get the address, cx = size of address buffer.
;exit with nc, cx = actual size of address, or cy if buffer not big enough.
	assume	ds:code
	mov	cx,0
	clc
	ret


	public	set_address
set_address:
;set the address of the interface.
;enter with es:di -> place to get the address, cx = size of address buffer.
;exit with nc, cx = actual size of address, or cy if buffer not big enough.
	assume	ds:nothing
	clc
	ret


rcv_mode_3:
;receive mode 3 is the only one we support, so we don't have to do anything.
	ret


	public	set_multicast_list
set_multicast_list:
;enter with ds:si ->list of multicast addresses, cx = number of addresses.
;return nc if we set all of them, or cy,dh=error if we didn't.
	mov	dh,NO_MULTICAST
	stc
	ret


	public	get_multicast_list
get_multicast_list:
;return with nc, es:di ->list of multicast addresses, cx = number of bytes.
;return	cy, NO_ERROR if we don't remember all of the addresses ourselves.
;return cy, NO_MULTICAST if we don't implement multicast.
	mov	dh,NO_MULTICAST
	stc
	ret


	public	terminate
terminate:
	ret

	public	reset_interface
reset_interface:
;reset the interface.
	assume	ds:code
	ret


;called	when we	want to determine what to do with a received packet.
;enter with cx = packet length, es:di -> packet type, dl = packet class.
	extrn	recv_find: near

;called after we have copied the packet into the buffer.
;enter with ds:si ->the packet, cx = length of the packet.
	extrn	recv_copy: near

	extrn	count_in_err: near
	extrn	count_out_err: near
	
	public	recv

recv:
;called from the recv isr.  All registers have been saved, and ds=cs.
;Upon exit, the interrupt will be acknowledged.
	assume	ds:code
recv_2:
	in_chip_reg		board_w_intreg,0	; get interrupt source

	test	al,intreg_hdlc0		;check if HDLC0
	jne	not_hdlc0

	jmp	which_reg		;Jump and check the chip

not_hdlc0:
	pr_ch_al	'+'
	ret			;Not this chip so give up

which_reg:
	in_chip_reg		board_w_hdlc0,hdlc_br_intflag
	mov	hdlc0_data.copy_intflag,al	;save the interrupt flags
;
;	We now analyse for the different interrupts that
;	may be present. We first make some tests for styles
;	of interrupts so we dont need to check everything
;	each time.
;
	test	hdlc0_data.copy_intflag,hdlc_intflag_eopd or hdlc_intflag_fa or hdlc_intflag_rx1519 or hdlc_intflag_rxoflw
	je	test_tint	;No receive style interrupts so jump

; There is a receive event
;
;	We recognise 8 receive events.
;	1/.	RX1519
;	2/.	RX1519 + RXOFLW
;	3/.	EOPD
;	4/.	EOPD+RX1519
;	5/.	EOPD + RX1519 + RXOFLW
;	6/.	FA + EOPD
;	7/.	FA + EOPD + RX1519
;	8/.	FA + EOPD + RX1519 + RXOFLW
;	We believe that other (potential) RX events cannot
;	occur. 
;	We recognise only three (major) states for the RX
;	protocol engine.
;	1/.	SKIPPING for the start of a packet; all data
;		is essentially ignored in this state. No
;		receive buffer will have been allocated.
;		We are in this state between packets or perhaps
;		because we have just run out of buffers!
;		See also state 3 below.
;	2/.	BUILDING a packet. Generally speaking,
;		providing we have enough room, 'packet' bytes
;		are just added into the buffer, 'good last' bytes
;		terminating the building of a packet and provoke
;		its delivery to the upper layer. Other data such
;		as 'bad last' or 'first' bytes cause the packet
;		being built to be discarded and the engine to
;		move to SKIPPING state. A first byte implies
;		a frame abort has been received of course.
;	3/.	It is also possible to be SKIPPING&BUILDING !
;		This occurs after we have read data that
;		would have moved us to BUILDING state but
;		we could not get buffer space. We need this
;		to properly detect frame aborts (see below).
;
;	EVENT PROCESSING in a little more detail.
;
;	1/. RX1519
;	We are required to clear 14 bytes from the fifo.
;	It is possible to show that in pathological
;	circumstances bytes other than packet bytes can
;	be in the FIFO!
;
;	2/.	RX1519 + RXOFLW
;	Regardless of state, we read 19 bytes from the FIFO.
;	We do this as we do not want to accidently
;	discard the front of a following packet.
;
;	3/.	EOPD
;	We process bytes up to a bad/good last byte.
;	We then deliver or discard the packet.
;	Clearly should not process more than 19 and
;	if RX1519 set it would be surprising if we
;	processed more than 14!
;
;	4/.	EOPD + RX1519
;	We are required to clear 14 bytes from the fifo.
;	Process as in 3/. above but make sure we clear
;	at LEAST 14 bytes. We might read more than 15
;	if for instance the EOPD occurred as the 16 byte
;	which had arrived while we were responding to
;	the event which interrupted us which was a 'packet'
;	byte in number 15. It is also possible that the 'last'
;	byte might be number 14 but a 'first' byte also arrived
;	in 15 creating the RX1519 to occur as well.
;
;	5/.	EOPD + RX1519 + RXOFLW
;	The RXOFLW implies we have missed stuff and some
;	data failed to get into the end of the FIFO. The
;	RX FIFO is supposed to enter a FLAG search mode after
;	overflowing. Thus is we process as in 3/. but EMPTY
;	the whole buffer(i.e. read 19 bytes)., 
;
;	6,7,8/.	I.E. any FA condition.
;	We will read upto 19 bytes of data. It would
;	be surprising if we read more than 14 unless RX1519
;	was also set. We will end when we discover a Frame
;	Abort condition. This is implied by a first byte
;	when building but ALSO by a first byte if skipping
;	and we have already seen a first byte!
;
;
;	Now set up some values. The maximum and minimum
;	count values, the current number read
;	and we clear the stop_status_mask.

;	mov	minimum_read,1	;often increased below
;	mov	maximum_read,14	;typically we end before this
;	mov	stop_status_mask,0	;clear the stop status mask
;
;test_fa:
;	test	hdlc0_data.copy_intflag,hdlc_intflag_fa
;	je	test_eopd
	; We have an FA so change stop_status_mask
	; and change the maximum read to 19 (?)
;	mov	stop_status_mask,mask_k_fabort
;	mov	maximum_read,19	;typically we end before this
;	jmp	test_rxoflw	;can avoid the eopd check
;
;test_eopd:
;	test	hdlc0_data.copy_intflag,hdlc_intflag_eopd
;	je	test_rxoflw
;	; Its eopd so update stop_status_mask
;	; and change the maximum read to 19 (?)
;	mov	stop_status_mask,mask_k_good or mask_k_bad
;	mov	maximum_read,19	;typically we end before this
;
;test_rxoflw:
;	test	hdlc0_data.copy_intflag,hdlc_intflag_rxoflw
;	je	test_rx1519
;	; Its Overflow so adjust minimum read number
;	; and change the maximum read to 19 (?)
;	mov	minimum_read,19
;	mov	maximum_read,19	;typically we end before this
;	jmp	test_rx_end	;can avoid the rx1519 check
;
;test_rx1519:
;	test	hdlc0_data.copy_intflag,hdlc_intflag_rx1519
;	je	test_rx_end
;	; Its Rx1519 so adjust the minimum read number
;	mov	minimum_read,14
;
test_rx_end:
;
;WE DONT CARE WHAT CAUSED THE INTERRUPT NOW!
;
	; Now we call the routine to process the RX fifo having
	; hopefully set up all the correct conditions.

	call	rint_process

test_tint:
	mov	al,hdlc0_data.copy_intflag
	and	al,hdlc0_data.copy_intenable ; ignore any not expected
	mov	hdlc0_data.copy_intflag,al

	test	hdlc0_data.copy_intflag,hdlc_intflag_txdone or hdlc_intflag_tx419 or hdlc_intflag_txurun
	je	test_ga	;No transmit style interrupts so jump

; Its some sort of transmit event

	test	hdlc0_data.copy_intflag,hdlc_intflag_txurun
	je	test_txdone
	call	tint_txurun; its an underrun event
;	Now need to avoid the txdone and tx419 code etc ....
	jmp	test_ga

test_txdone:
	test	hdlc0_data.copy_intflag,hdlc_intflag_txdone
	je	test_tx419
	call	tint_txdone; its packet trans. complete event
	jmp test_ga			; NOTE tx419 will always be set
		;when txdone is set! As we have processed
		;the outgoing packet we must now NOT go
		;through the tx419 code as well! IMPORTANT!

test_tx419:
	test	hdlc0_data.copy_intflag,hdlc_intflag_tx419
	je	test_ga
	call	tint_tx419; its a tx fifo low event

; and carry on...

test_ga:
	test	hdlc0_data.copy_intflag,hdlc_intflag_ga
	je	int_fin

;	Its a Go-ahead .. We should not get these..

	pr_ch_al	'-'

int_fin:

;	Now we have finished.. Just output H so we can check

	ret

;
;Process 8952 Receive interrupts
;

;	Process all RX FIFO data

rint_process:	
;	pr_ch_al	'A'

;	mov	number_read,0	;none so far...
;	mov	rint_status_mask,0	;clear the rint status mask

	push	ds			; get set up for the routine
	pop	es

rint_loop:
;	NEED FIXES IN HERE
;	mov	al,number_read
;	cmp	maximum_read,al	;check if data still due
;	jg	rint_some_due	;yes there is
;	pr_ch_al	'B'
;	ret				;no we have finished

;rint_some_due:

	;get fifo status and build into a mask
	;must first check that there is still some data left..
	; NOTE it is important that we dont get here less than
	; one microsecond after we last removed data.

	in_chip_reg		board_w_hdlc0,hdlc_br_fifostatus
	test	al,hdlc_fifostatus_RXFIFO
	jne	rint_fifo_not_empty
	ret					; we have now emptied the RX FIFO
						; so we return...
rint_fifo_not_empty:		;still some data so analyze...
	mov	cl,6		;number of bits to shift
	and	al,hdlc_fifostatus_RXBYTE ;get RX byte status
	shr	al,cl		;shift to lower two bits
	mov	cl,al		;transfer to cl
	mov	al,00000001b	;set low bit in al
	shl	al,cl			;and shift to correct bit for mask
	mov	byte_status_mask,al	;save byte status mask
;	or	rint_status_mask,al	;save rint segment status mask

;
;	Now check our state
;
	test	hdlc0_data.rxisr_w_state,state_k_skipping
	jz	rint_building	;must be building alone
	jmp	rint_skipping	;skipping or skipping&building

;
;	Definitely building a packet
;
rint_building:

	test	byte_status_mask,mask_k_good;good last byte ?		
	jnz	rint_build_good_last			;deal with good last byte

	test	byte_status_mask,mask_k_bad;bad last byte ?		
	jnz	rint_build_bad_last			;deal with bad last byte

	test	byte_status_mask,mask_k_first;first byte ?		
	jnz	rint_build_first			;deal with first byte

;
;	must be packet byte
;
	in_chip_reg		board_w_hdlc0,hdlc_br_receive
	mov	di,hdlc0_data.rxisr_w_byte ;get ptr to next byte
	stosb			;store char into buffer
	mov	hdlc0_data.rxisr_w_byte,di ;save ptr to next byte
	
	inc	hdlc0_data.rxisr_w_count			;count for all isr calls
;	inc	number_read			;count for this isr

	jmp	rint_end

rint_build_good_last:
;
;	good last byte coming, get it, then close
;	the packet and assign it to queue ownership etc.
;	then bump the pointers
;
;	pr_ch_al	'C'
	in_chip_reg		board_w_hdlc0,hdlc_br_receive
	mov	di,hdlc0_data.rxisr_w_byte ;get ptr to next byte
	stosb			;store char into buffer
	mov	hdlc0_data.rxisr_w_byte,di ;save ptr to next byte
	inc	hdlc0_data.rxisr_w_count			;count for all isr calls
;	inc	number_read			;count for this isr

	mov	bx,hdlc0_data.rxisr_w_pkt	;get pointer to buffer

	mov	ax,hdlc0_data.rxisr_w_count ;save count in buffer

	mov	[bx].buff_w_size,ax ;via ax

	mov	[bx].buff_w_owner,owner_k_queue	;mark in q
	mov	bx,[bx].buff_w_next	;get pointer to next
	mov	hdlc0_data.rxq_w_back,bx	;and save as back of queue
;
;	and change state
;
	mov	hdlc0_data.rxisr_w_state,state_k_skipping

	jmp	rint_end

rint_build_bad_last:

;
;	Now deal with bad FCS, read the bad byte and then
;	then discard the packet we have got so far.
;
	pr_ch_al	'D'
	in_chip_reg		board_w_hdlc0,hdlc_br_receive
				;get char but ignore
;	inc	number_read			;count for this isr
	mov	bx,hdlc0_data.rxisr_w_pkt	;get ptr to buffer
	call	rint_reset	;release the buffer etc
;
;	and change state
;
	mov	hdlc0_data.rxisr_w_state,state_k_skipping
	jmp rint_end

rint_build_first:
;
;	Now deal with first byte, this must be a frame abort!
;	DONT read the byte, leave it there for next iteration
;	to use as part of the next packet.
;	Discard the packet we have got so far.
;
	pr_ch_al	'E'
	mov	bx,hdlc0_data.rxisr_w_pkt	;get ptr to buffer
	call	rint_reset	;release the buffer etc
;
;	Record the Frame Abort in the rint_status_mask
;
;	or	rint_status_mask,mask_k_fabort
;
;	and change state
;
	mov	hdlc0_data.rxisr_w_state,state_k_skipping
	jmp rint_end

rint_skipping:
;
;	If we are here then we must be in between packets.
;	It is possible that we may have run out of buffers
;	so we may be actually discarding data that would
;	otherwise have been good. If this second situation
;	exists then both the building and skipping bits
;	are set in the state mask.
;

	test	byte_status_mask,mask_k_good;good last byte ?		
	jnz	rint_skip_good_last			;deal with good last byte

	test	byte_status_mask,mask_k_bad;bad last byte ?		
	jnz	rint_skip_bad_last			;deal with bad last byte

	test	byte_status_mask,mask_k_first;first byte ?		
	jnz	rint_skip_first			;deal with first byte

;
;	must be packet byte
;
	in_chip_reg		board_w_hdlc0,hdlc_br_receive
				;get char but ignore
;	inc	number_read			;count for this isr
;
;	Well we are certainly skipping past bytes now.
;
	mov	hdlc0_data.rxisr_w_state,state_k_skipping or state_k_building

	jmp	rint_end

rint_skip_good_last:
;
;	good last byte coming, get it, and ignore
;
	pr_ch_al	'F'
	in_chip_reg		board_w_hdlc0,hdlc_br_receive
				;get char but ignore
;	inc	number_read			;count for this isr

;
;	change state (might have been skipping and building)
;
	mov	hdlc0_data.rxisr_w_state,state_k_skipping

	jmp	rint_end

rint_skip_bad_last:

;
;	Now deal with bad FCS, read the bad byte and ignore
;
	pr_ch_al	'G'
	in_chip_reg		board_w_hdlc0,hdlc_br_receive
				;get char but ignore
;	inc	number_read			;count for this isr
;
;	change state (might have been skipping and building)
;
	mov	hdlc0_data.rxisr_w_state,state_k_skipping
	jmp rint_end

rint_skip_first:

;
;	Now deal with first byte, this must be the start
;	of the next packet.
;
	test	hdlc0_data.rxisr_w_state,state_k_building
	jnz	rint_skip_build	;skipping&building
;
;	As we are here we are just skipping at the moment.
;	As we have found a first byte, we now try to
;	get a buffer in which to build the new packet.
;
	call	rint_get_buffer
	jnc	rint_skip_first_got_buffer
;
;	We failed to get buffer. We thus must discard
;	the incoming data, count it and move to the
;	skipping&building state.
;
	pr_ch_al	'H'
	in_chip_reg		board_w_hdlc0,hdlc_br_receive
				;get char but ignore
;	inc	number_read			;count for this isr
;
;	and change state
;
	mov	hdlc0_data.rxisr_w_state,state_k_skipping or state_k_building
	jmp rint_end

rint_skip_first_got_buffer:
;	pr_ch_al	'I'
	in_chip_reg		board_w_hdlc0,hdlc_br_receive
	mov	di,hdlc0_data.rxisr_w_byte ;get ptr to next byte
	stosb			;store char into buffer
	mov	hdlc0_data.rxisr_w_byte,di ;save ptr to next byte
	
	inc	hdlc0_data.rxisr_w_count			;count for all isr calls
;	inc	number_read			;count for this isr
;
;	and change state
;
	mov	hdlc0_data.rxisr_w_state,state_k_building
	jmp	rint_end

rint_skip_build:
;
;	We are skipping and building. I.e. we are
;	discarding data that probably would have been
;	good but we had no buffers available.
;	Thus if we find a first byte this must be a frame
;	abort. Deal with it as such, leaving the byte in
;	the FIFO to be picked up on the next cycle.
;
;	Record the Frame Abort in the rint_status_mask
;
	pr_ch_al	'J'
;	or	rint_status_mask,mask_k_fabort
;
;	and change state
;
	mov	hdlc0_data.rxisr_w_state,state_k_skipping

	jmp rint_end

rint_end:
;
;	Now is the time to tidy up at the end of
;	the rint loop. Several things to check. For instance,
;	if we are still building and not skipping then
;	if the packet is already full we have a problem.
;	The best policy must be to discard and move
;	to skipping&building state.
;
;	We must also check if we need to stop iterating.
;	If we have not yet read the minimum_read number
;	of bytes we must go again. If we have, then
;	unless we have got a rint_status_mask that has 
;	at least one bit in common with our stop_status_mask
;	we must also loop again.

	test	hdlc0_data.rxisr_w_state,state_k_skipping
	jnz	rint_info_size_ok	;dont care...

	cmp	hdlc0_data.rxisr_w_count,info_k_size	;check if room for more info
	jl	rint_info_size_ok	;and branch if ok
;
;	TOO much data in this packet, the next byte
;	even if it were a last byte would overfill
;	the info area. We must therefore discard the packet
;	and move to the skipping&building state.
	pr_ch_al	'K'
	mov	bx,hdlc0_data.rxisr_w_pkt	;get ptr to buffer
	call	rint_reset	;release the buffer etc
;
;	and change state
;
	mov	hdlc0_data.rxisr_w_state,state_k_skipping or state_k_building

rint_info_size_ok:
;	mov	al,number_read
;	cmp	minimum_read,al	;have we read enough?
;	jle	rint_enough			;yes
	jmp	rint_loop				; no go read some more..

;rint_enough:
;	mov	al,stop_status_mask
;	test	rint_status_mask,al	;stop state?
;	jnz	rint_stop_state		;yes
;	jmp	rint_loop				; no go read some more..

;rint_stop_state:
;
;	Well that seems to be it for this call to the RX
;	interrupt service routine so bye bye...

;	ret
	
;
;	Routine to get a buffer (or not)
;

rint_get_buffer:
;	pr_ch_al	'L'

	mov	bx,hdlc0_data.rxq_w_back	;get back of queue
	cmp	[bx].buff_w_owner,owner_k_empty	;is it empty?
	jne	cant_get_buffer

	call rint_grab
	clc		;all o.k.
	ret
cant_get_buffer:
	pr_ch_al	'M'
	stc		;signal error (well at least no buffers left)
	ret

rint_grab:
	or	bx,bx
	jz	rint_grab_problem
	mov	[bx].buff_w_owner,owner_k_isr	;mark inuse by isr
	mov	hdlc0_data.rxisr_w_pkt,bx	;save for isr to use next
	lea	ax,[bx].buff_info	;get address of new info area
	mov	hdlc0_data.rxisr_w_byte,ax	;save for isr
	mov	hdlc0_data.rxisr_w_count,0	;and clear count
;	Now set the state to mark as BUILDING
	mov	hdlc0_data.rxisr_w_state,state_k_building
	clc
	ret
rint_grab_problem:
	pr_ch_al	'N'
	stc
	ret

rint_reset:
	or	bx,bx
	jz	rint_reset_problem
	mov	[bx].buff_w_owner,owner_k_empty	;mark empty
	clc
	ret
rint_reset_problem:
	pr_ch_al	'O'
	stc
	ret

; --------------------------------------------------------------
;
;  recv_exiting
;
	public	recv_exiting
recv_exiting:
	push	ax
	push	bx
	mov	bx,hdlc0_data.rxq_w_front		;get pointer to next buffer
	cmp	[bx].buff_w_owner,owner_k_queue	;belongs to q?
	jne	recv_exiting_exit           ; no - skip to end
	push	cx
	push	dx
	push	ds
	push	es
	push	bp
	push	di
	push	si
	push	cs			; point ds properly
	pop	ds
	cmp	hdlc0_data.rxupcall_w_state,upcall_k_idle	;is receive frame already active?
	jne	already_active		;frame will be caught so jump
	mov	hdlc0_data.rxupcall_w_state,upcall_k_active	;else mark recv_frame starting
	sti				; enable interrupts

	call	recv_frame

	cli
already_active:
	pop	si
	pop	di
	pop	bp
	pop	es
	pop	ds
	pop	dx
	pop	cx
recv_exiting_exit:
	pop	bx
	pop	ax
	ret


; --------------------------------------------------------------
;
;  recv_frame
;
  ifdef debug
	public recv_frame
  endif
recv_frame:
;	pr_ch_al	'P'

	mov	bx,hdlc0_data.rxq_w_front		;get pointer to next buffer

recv_frame_2:
	lea	si,[bx].buff_info		;point to data
	mov	cx,[bx].buff_w_size	;get its size
	jcxz	recv_frame_3		;count zero? yes,just free frame.
;we don't need to set the type because none are defined for our HDLC encoding.
	push	si			;save si in case we reject it.
	push	bx
	mov	di,0			;but we avoid any segment end bullshit.
	mov	dl,cs:driver_class
	call	recv_find		;look up our type.
	pop	bx
	pop	si

	mov	ax,es			;is this pointer null?
	or	ax,di
	je	recv_frame_3		;yes - just free the frame.
;	pr_ch_al	'Q'
	push	cx
	push	es			;remember where the buffer pointer is.
	push	di

	rep	movsb	;and copy our packet into users buffer

	pop	si			;now give the frame to the client.
	pop	ds
	pop	cx
;	pr_ch_al	'R'
	assume	ds:nothing

	call	recv_copy
	push	cs
	pop	ds
	pr_ch_al	'S'
	assume	ds:code

recv_frame_3:
	mov	[bx].buff_w_owner,owner_k_empty	;free the buffer
	mov	bx,[bx].buff_w_next		;get pointer to next
	mov	hdlc0_data.rxq_w_front,bx		;adjust front of q

	cmp	[bx].buff_w_owner,owner_k_queue	;belongs to q?
	je	recv_frame_2		; yes so process this one.
	mov	hdlc0_data.rxupcall_w_state,upcall_k_idle	;else mark recv_frame as inactive
;	pr_ch_al	'T'
	ret

;Handle 8952 transmitter interrupts

; --------------------------------------------------------------

tint_txurun:
	pr_ch_al	'd'
	mov	bx,hdlc0_data.txisr_w_pkt	;point to the packet buffer
	call	tint_reset	;reset pointers etc...
	ret


tint_tx419:
;
; - for MT8952B fifo stuff up to 15 chars at a time
;
;	NOW NEED TO POINT DX at TX FIFO
;
	mov	dx,board_w_hdlc0		;make dx point at transmit fifo
	add	dx,hdlc_bw_transmit
	mov	cx,15                   ;fifo fill-loop counter
	mov	bx,hdlc0_data.txisr_w_count	;get count of bytes left
	mov	si,hdlc0_data.txisr_w_byte	;get pointer to next byte
tint_next:
	lodsb				;fetch next char
	dec	bx			;reduce count of remaining bytes
	jne	not_send_last
	push	dx				;save dx
	mov	dx,board_w_hdlc0
	add	dx,hdlc_b_control
	push	ax
	in	al,dx				;get current hdlc control reg
	or	al,hdlc_control_eop	;mark as end of packet
	out	dx,al				;and tell the 8952
	pop	ax				; restore al
	pop	dx				;and dx

not_send_last:
	out	dx,al			;output char
	or	bx,bx		;any more chars to output
	je	tint_no_more        ;none...
	loop	tint_next		;loop while fifo not full
;
;	Still some more so select TXURUN and TX419 ints enabled
;
	pr_ch_al	'e'
	mov	dx,board_w_hdlc0
	add	dx,hdlc_bw_intenable
;	in	al,dx				;get current hdlc intenable
	mov	al,hdlc0_data.copy_intenable
;
;	switch off txdone interrupt and enable tx419 and txurun
;
	and	al,not hdlc_intenable_txdone
	or	al,hdlc_intenable_tx419 or hdlc_intenable_txurun
	out	dx,al				;and tell the 8952
	mov	hdlc0_data.copy_intenable,al		;and save it

	mov	hdlc0_data.txisr_w_count,bx	;save count of bytes left
	mov	hdlc0_data.txisr_w_byte,si	;save pointer to next byte
	clc		;clear carry, all ok
	ret		;and exit

tint_no_more:
;
;	No more so select TXDONE and TXURUN ints enabled only
;
	pr_ch_al	'f'
	mov	dx,board_w_hdlc0
	add	dx,hdlc_bw_intenable
;	in	al,dx				;get current hdlc intenable
	mov	al,hdlc0_data.copy_intenable
;
;	switch off tx419 interrupt and enable txurun and txdone 
;
	and	al,not hdlc_intenable_tx419
	or	al,hdlc_intenable_txdone or hdlc_intenable_txurun
	out	dx,al				;and tell the 8952
	mov	hdlc0_data.copy_intenable,al		;and save it

	mov	hdlc0_data.txisr_w_count,bx	;save count of bytes left
	mov	hdlc0_data.txisr_w_byte,si	;save pointer to next byte
	clc		;clear carry if all ok
	ret		;and exit

;No more characters to transmit -- disable transmit interrupts.

tint_txdone:
;	pr_ch_al	'g'
	mov	bx,hdlc0_data.txisr_w_pkt	;get pointer to isrs pkt
	or	bx,bx				;check if pkt pointer is 0
	je	tint_skip
	mov	[bx].buff_w_owner,owner_k_empty;set owned by empty
tint_skip:
	call	tint_new	;try to move to next buffer
	jc	tint_all_empty	;if carry set then none left
	clc
	ret

tint_all_empty:
;
;	No more so set all tx ints off
;
;	pr_ch_al	'h'
	mov	dx,board_w_hdlc0
	add	dx,hdlc_bw_intenable
;	in	al,dx				;get current hdlc intenable
	mov	al,hdlc0_data.copy_intenable
;
;	switch off tx419, txurun and txdone 
;
	and	al,not (hdlc_intenable_tx419 or hdlc_intenable_txdone or hdlc_intenable_txurun)
	out	dx,al				;and tell the 8952
	mov	hdlc0_data.copy_intenable,al		;and save it

	xor	ax,ax		;clear ax
	mov	hdlc0_data.txisr_w_pkt,ax	;save in pointer to buffer
	ret

;
;	Routine to get info for next packet from queue
;
tint_new:
	mov	bx,hdlc0_data.txq_w_front	;get pointer to front of queue
	cmp	[bx].buff_w_owner,owner_k_queue	;belongs to q?
	jne	tint_no_buffers_in_queue
;
;	now bump the front of queue
;
	pr_ch_al	'i'
	mov	ax,[bx].buff_w_next	;point to buffer
	mov	hdlc0_data.txq_w_front,ax	;adjust front of queue
;
	call	tint_reset	;and set the pointers etc.
	ret

tint_reset:
	or	bx,bx
	jz	tint_reset_problem
	mov	[bx].buff_w_owner,owner_k_isr;set owned by the isr
	mov	ax,[bx].buff_w_size	;get size of data unit
	mov	hdlc0_data.txisr_w_count,ax	;and save it for us
	mov	hdlc0_data.txisr_w_pkt,bx	;save pointer to buffer
	lea	ax,[bx].buff_info	;get address of data unit
	mov	hdlc0_data.txisr_w_byte,ax	;and save for us
	call	tint_tx419		;and pretend we had a tx419
	ret
tint_reset_problem:
	stc
	ret
tint_no_buffers_in_queue:
	pr_ch_al	'j'
	stc		;set carry - could not do it
	ret

;Set bit(s) in I/O port
setbit:
;enter with dx = port, ah = bit to set.
	in	al,dx
	or	al,ah
	out	dx,al
	ret


;Clear bit(s) in I/O port
clrbit:
;enter with dx = port, ah = bit to set.
	in	al,dx
	not	al			;perform an and-not using DeMorgan's.
	or	al,ah
	not	al
	out	dx,al
	ret

;
;	Now define some buffers for the rings.
;	Do it statically now because its easier.
;
;	First the transmit ring
;
t1_buff	buff	<offset t8_buff, offset t2_buff>
t2_buff	buff	<offset t1_buff, offset t3_buff>
t3_buff	buff	<offset t2_buff, offset t4_buff>
t4_buff	buff	<offset t3_buff, offset t5_buff>
t5_buff	buff	<offset t4_buff, offset t6_buff>
t6_buff	buff	<offset t5_buff, offset t7_buff>
t7_buff	buff	<offset t6_buff, offset t8_buff>
t8_buff	buff	<offset t7_buff, offset t1_buff>
;
;	Now the Receive ring
;
r1_buff	buff	<offset r8_buff, offset r2_buff>
r2_buff	buff	<offset r1_buff, offset r3_buff>
r3_buff	buff	<offset r2_buff, offset r4_buff>
r4_buff	buff	<offset r3_buff, offset r5_buff>
r5_buff	buff	<offset r4_buff, offset r6_buff>
r6_buff	buff	<offset r5_buff, offset r7_buff>
r7_buff	buff	<offset r6_buff, offset r8_buff>
r8_buff	buff	<offset r7_buff, offset r1_buff>
;
;	include the serial trace output subroutines
;

	include	sersub.asm

;any code after this will not be kept after initialization.
end_resident	label	byte

	public	usage_msg
usage_msg	db	"usage: EXPRESS packet_int_no [-n] [driver_class] [int_no] ",CR,LF
		db	"   -n instructs card to be an NT",CR,LF
		db	"   The driver_class should be SLIP or a number.",CR,LF,'$'

	public	copyright_msg
copyright_msg	db	"Packet driver for MITEL EXPRESS CARD, version ",'0'+(majver / 10),'0'+(majver mod 10),".",'0'+version,CR,LF
		db	"Portions Copyright 1988 Phil Karn",CR,LF
		db	"ISDN bits by Dave Price and Bob Gautier",CR,LF,'$'

class_name_ptr	dw	?
class_name	db	"Interface class ",'$'
slip_name	db	"SLIP",CR,LF,'$'
int_no_name	db	"Interrupt number ",'$'
express_start	db	"starting to initial EXPRESS card",CR,LF,'$'
express_finish	db	"completed initialization of EXPRESS card",CR,LF,'$'

	extrn	set_recv_isr: near

;enter with si -> argument string, di -> word to store.
;if there is no number, don't change the number.
	extrn	get_number: near

;enter with dx -> name of word, di -> dword to print.
	extrn	print_number: near

;enter with si -> argument string.
;skip spaces and tabs.  Exit with si -> first non-blank char.
	extrn	skip_blanks: near


	public	parse_args
parse_args:
;exit with nc if all went well, cy otherwise.
	call	skip_blanks
	cmp	al,'-'			;did they specify a switch?
	jne	not_switch
;
;	Only SUPPORT -n SWITCH ARGUMENT AT THE MOMENT
;
	cmp	byte ptr [si+1],'n'	;did they specify '-n'?
	je	got_NT_switch
	stc				;no, must be an error.
	ret
got_NT_switch:
	mov	NT_switch,1
	add	si,2			;skip past the switch's characters.
	jmp	parse_args		;go parse more arguments.
not_switch:
	or	al,20h			;convert to lower case (assuming letter).
parse_args_2:
	cmp	al,'s'
	jne	parse_args_3
	mov	driver_class,6		;SLIP, from packet spec.
	mov	dx,offset slip_name
	jmp	short parse_args_1
parse_args_3:
	mov	di,offset driver_class
	call	get_number
	mov	class_name_ptr,0
	jmp	short parse_args_6
parse_args_1:
	mov	class_name_ptr,dx
parse_args_5:
	mov	al,[si]			;skip to the next blank or CR.
	cmp	al,' '
	je	parse_args_6
	cmp	al,CR
	je	parse_args_6
	inc	si			;skip the character.
	jmp	parse_args_5
parse_args_6:
	mov	di,offset int_no
	call	get_number
;
;	Might get number of buffer in TX queue here.
;
	clc
	ret


; --------------------------------------------------------------
;
;  etopen
;

	public	etopen
etopen:
	pushf
	cli

	call	open	; open the serial port for traces

	;let user know we are about to initial
	; the express card

	pr_ch_al	'$'
;
;Now set up the Mitel Express Card
;
;
;	First the dphone - this controls timing as well
;
	out_chip_reg_value	board_w_dphone,dphone_b_test,dphone_test_disable
;
;	Now the board timing via the dphone sense/drive port
;
	out_chip_reg_value	board_w_dphone,dphone_b_sddir,dphone_sddir_allout
	cmp	NT_switch,1	;has user selected NT operation
	jne	act_as_te		;no so set as TE
	out_chip_reg_value	board_w_dphone,dphone_b_sddata,dphone_sddata_nt
	jmp	te_nt_set

act_as_te:
	out_chip_reg_value	board_w_dphone,dphone_b_sddata,dphone_sddata_te

te_nt_set:

;
;	Now set use of st-bus timeslots
;
	out_chip_reg_value	board_w_dphone,dphone_b_time,<dphone_time_c or dphone_time_pcmb1>
;
;	Now stop the watchdog
;
	out_chip_reg_value	board_w_dphone,dphone_b_wdog,0
;
;	Now set the tone values
;
	out_chip_reg_value	board_w_dphone,dphone_b_tone1,dphone_tone_697
	out_chip_reg_value	board_w_dphone,dphone_b_tone2,dphone_tone_1209
;
;	Now set up the dsp
;
	out_chip_reg_value	board_w_dphone,dphone_b_dsp,<dphone_dsp_cpcmen or dphone_dsp_dpcmen or dphone_dsp_dual>
;
;	Now set up the transducers
;
	out_chip_reg_value	board_w_dphone,dphone_b_trans,<dphone_trans_side or dphone_trans_hsmic or dphone_trans_hsskr>
;
;	Finally the Receive gain control
;
	out_chip_reg_value	board_w_dphone,dphone_b_rgain,dphone_rgain_rfg_m7
;
;	Second the snic
;
	out_chip_reg_value	board_w_snic,snic_b_master,<snic_master_cstenable or snic_master_msdisable or snic_master_irqenable>
	out_chip_reg_value	board_w_snic,snic_b_stbus,snic_stbus_all
;
;	Now set up the hdlc controller
;
	out_chip_reg_value	board_w_hdlc0,hdlc_b_time,hdlc_time_rst
;
;	NOTE you are required to clear reset TWICE
;
	out_chip_reg_value	board_w_hdlc0,hdlc_b_time,<hdlc_time_ic or hdlc_time_brck or hdlc_time_c2bits8>
	out_chip_reg_value	board_w_hdlc0,hdlc_b_time,<hdlc_time_ic or hdlc_time_brck or hdlc_time_c2bits8>

	out_chip_reg_value	board_w_hdlc0,hdlc_b_control,<hdlc_control_rxen or hdlc_control_txen>

	out_chip_reg_value	board_w_hdlc0,hdlc_b_raddress,00
	out_chip_reg_value	board_w_hdlc0,hdlc_bw_wdog,00

	out_chip_reg_value	board_w_hdlc0,hdlc_bw_intenable,<hdlc_intenable_eopd or hdlc_intenable_fa or hdlc_intenable_rx1519 or hdlc_intenable_rxoflw>
	mov	hdlc0_data.copy_intenable,al		;and save it

;
;	Now the DX; plug up the channels and send messages etc,
;

	dx_source	snic_stream,snic_b2_channel,dphone_stream,dphone_b1_channel
	dx_source	dphone_stream,dphone_b1_channel,snic_stream,snic_b2_channel

	dx_source	snic_stream,snic_b1_channel,hdlc_stream,hdlc_b1_channel
	dx_source	hdlc_stream,hdlc_b1_channel,snic_stream,snic_b1_channel

;
;	MIGHT NEED TO CHANGE THE NEXT FOR NT OPERATION
;

	dx_message	snic_stream,snic_c_channel,<<snic_c_ar or snic_c_clrdia>>

	;let user know we have finished
	; initializing the express card

	pr_ch_al	'%'

;Set interrupt vector to EXPRESS handler

	call	set_recv_isr

	mov	dx,offset end_resident

	push	dx			;save the ending address.

	pop	dx			;return the ending address.
	popf
	clc				;indicate no errors.
	ret


	public	print_parameters
print_parameters:
	cmp	class_name_ptr,0
	je	echo_args_1

	mov	dx,offset class_name
	mov	ah,9
	int	21h
	mov	dx,class_name_ptr
	mov	ah,9
	int	21h
	jmp	short echo_args_2
echo_args_1:
	mov	di,offset driver_class
	mov	dx,offset class_name
	call	print_number
echo_args_2:

	mov	di,offset int_no
	mov	dx,offset int_no_name
	call	print_number

	ret

code	ends

	end
[ RETURN TO DIRECTORY ]