www.pudn.com > tcpipstack.rar > XSIO.MAC


;XSIO.MAC - Z-80 SIO interrupt-driven serial port driver for TinyTCP 
; 
;|===================================================================| 
;|  My changes can be considered public domain.  Geof's statement    | 
;|  will cover everything.                                           | 
;|              - Rick Rodman 09/02/97                               | 
;|===================================================================| 
; 
;	940807	rr	orig version (Xerox 820 SIO/0) 
;	940925	rr	mods for Kaypro 
 
;For purposes of Tiny-TCP, the 128-byte FIFO may be too small.  
;It may need to be substantially larger. 
 
;int far inp_status( void ); 
;void far inp_flush( void ); 
;void far init_comm( void ); 
;void far uninit_comm( void ); 
;char far inp_char( void ); 
;void far outp_char( char c ); 
 
	.Z80 
	CSEG 
 
; ----- Eco-C notes ------------------------------------------------- 
 
; 1. Eco-C prefixes globals with a question mark. e.g. ?INP_STATUS:: 
; 2. Function preamble: 
;	ADD	HL,SP 
;	PUSH	HL		Not clear what this is for. 
; 3. Parameters are on the stack, at SP + 4, SP + 8, etc. +4 = last param. 
; 4. When done, put return value in HL and jump to $RETI##, 
;	or in A and jump to RETVAL##. 
; N.B. Generated source code puts value in HL and jumps to $RTNI##, 
;	(int), or jumps to $RTNV## (void). 
 
; ----- Conditionals ------------------------------------------------ 
 
XEROX820	EQU	0	;if assembling for Xerox 820 
KAYPRO		EQU	1	;if assembling for Kaypro 
 
;NOTES ON XEROX 820: 
;The Xerox 820 uses interrupts itself. Interrupts are in mode 2, with 
;the interrupt table at location FF00. 
 
;NOTES ON KAYPRO: 
;The Kaypro apparently doesn't use interrupts itself. In TCJ #67, Mr. 
;Rottenkolber explains that the interrupt vectors and all routines must 
;be above 4000 hex. 
;The Kaypro has two SIOs. The B channel of the one used for the modem 
;port is used for the keyboard.  See TCJ #56 also. 
 
POLLRECV	EQU	0 
POLLXMIT	EQU	1 
 
;NOTE: The interrupt transmit logic doesn't work. I'm not sure what's 
;wrong with it. Polled transmit and interrupt receive works well. 
 
; ----- Equates ----------------------------------------------------- 
 
	IF XEROX820 
SIO		EQU	4	;I/O port of Z-80 SIO/0 
SIOVEC		EQU	0FF08H	;interrupt vector for SIO (mode 2) 
BAUDPORT	EQU	0	;baud rate port for channel A (0C = B) 
B9600		EQU	0EH	;value for 9600 baud 
	ENDIF 
 
	IF KAYPRO 
SIO		EQU	4	;I/O port of Z80 SIO 
BAUDPORT	EQU	0	;baud rate port for channel A (0B = B) 
B9600		EQU	0EH	;A = 2400, C = 4800, E = 9600, F = 19200 
	ENDIF 
 
SIOADATA	EQU	SIO 
SIOACTRL	EQU	SIO+2 
SIOBDATA	EQU	SIO+1 
SIOBCTRL	EQU	SIO+3 
 
TXRDY		EQU	2	;tx ready on status 
RXRDY		EQU	0	;rx ready on status 
 
; ----- Initialization ---------------------------------------------- 
 
;void Far init_comm( void ); 
 
;?INIT_COMM::		;public entry point 
INIT?COMM:: 
	ADD	HL,SP 
	PUSH	HL 
 
	IF	KAYPRO 
	DI			;disable ints til we have them set up 
	ENDIF 
 
	LD	HL,SIOTABLE 
	LD	B,SIOTLEN 
SILOOP: 
	LD	A,(HL) 
	INC	HL 
	OUT	(SIOACTRL),A	;set A port 
	DJNZ	SILOOP 
 
	LD	A,1		;register 1 of B channel 
	OUT	(SIOBCTRL),A 
	LD	A,04H		;set Status Affects Vector on B channel. 
	OUT	(SIOBCTRL),A	;and disable any other interrupts 
 
	LD	A,2		;register 2 of B channel 
	OUT	(SIOBCTRL),A 
 
	LD	DE,SIOVEC	;get interrupt vector 
 
	IF	KAYPRO 
	LD	A,E 
	ADD	A,15 
	AND	0F0H		;move it to a 16-byte boundary 
	LD	E,A 
 
	CP	240		;fewer than 16 bytes left on the page? 
	JR	C,PAGEOK 
	LD	E,0		;no, so set E to zero, 
	INC	D		;and increment D 
PAGEOK: 
	LD	A,D		;get high-order byte 
	LD	I,A		;set interrupt register (page for mode 2) 
	ENDIF 
 
	LD	A,E		;get low-order byte 
	OUT	(SIOBCTRL),A	;set interrupt vector on chip 
 
	LD	HL,PROTOVEC	;copy prototype vector to where it goes 
	LD	BC,16		;bytes to move (8 vectors) 
	LDIR 
 
	IF	KAYPRO 
	IM	2		;set interrupt mode 2 
	EI 
	ENDIF 
 
	LD	A,B9600 
	OUT	(BAUDPORT),A	;set baud rate 
 
	XOR	A 
	LD	(XMITTING),A	;zero transmitting flag 
 
	JP	$RTNV## 
 
; ----- Prototype vector table -------------------------------------- 
 
PROTOVEC: 
;	CHANNEL B - not used (Xerox printer port, Kaypro keyboard) 
 
	DEFW	EI_RETI 
	DEFW	EI_RETI 
	DEFW	EI_RETI 
	DEFW	EI_RETI 
 
;	CHANNEL A - modem port 
 
	DEFW	SA_XMIT_INT		;Transmit interrupt 
	DEFW	SA_EXT_STA_INT		;Ext. Status interrupt 
	DEFW	SA_RECV_INT		;Receive interrupt 
	DEFW	SA_ERR_INT		;Error interrupt 
 
; ----- Serial I/O table -------------------------------------------- 
 
SIOTABLE: 
	DEFB	018H		;channel reset 
	DEFB	4+010H		;r4 + reset external status interrupt 
	DEFB	44H		;x16 clock, 1 stop bit 
				;01 - odd parity 
				;03 - even parity 
				;04 - 1 stop 
				;08 - 1.5 stop 
				;0C - 2 stop 
				;40 - x16 clock 
				;80 - x32 clock 
				;C0 - x64 clock	;was 4C 
	DEFB	3		;r3 
	DEFB	0C1H		;recv 8 bits and enable receiver 
				;01 - enable rx 
				;20 - auto enables 
				;C0 - receive 8 bits 
	DEFB	5		;r5 
	DEFB	068H		;trans 8 bits, trans enable 
				;02 - RTS on 
				;08 - tx enable 
				;60 - transmit 8 bits 
				;80 - dtr on 
	DEFB	1		;r1 
 
	IF	POLLRECV 
	IF	POLLXMIT 
	DEFB	0H		;no interrupts 
	ELSE 
	DEFB	02H		;xmit interrupt only 
	ENDIF 
	ELSE 
	IF	POLLXMIT 
	DEFB	18H		;recv interrupt only 
	ELSE 
	DEFB	1AH		;recv & xmit interrupt 
	ENDIF 
	ENDIF 
				;01 - enable ext status int 
				;02 - transmitter interrupt 
				;04 - status affects vector (B channel) 
				;18 - receiver interrupt 
SIOTLEN	EQU $-SIOTABLE 
 
; ----- external status interrupt ----------------------------------- 
 
SA_EXT_STA_INT: 
	PUSH	AF 
	LD	A,10H		;reset the external status interrupt 
	OUT	(SIOACTRL),A 
	POP	AF 
EI_RETI: 
	EI 
	RETI 
 
; ----- error interrupt --------------------------------------------- 
 
SA_ERR_INT: 
	PUSH	AF 
	IN	A,(SIOADATA)	;read junk data 
	LD	A,30H 
	OUT	(SIOACTRL),A	;reset error flags 
	POP	AF 
	EI 
	RETI 
 
; ===== FIFO ROUTINES =============================================== 
 
FIFOSIZE	EQU	128	;must be power of 2, and less than 256  
 
;Put Offset is offset of last byte put in FIFO. 
;	It is incremented before putting the next byte in the FIFO. 
;Get Offset is offset of last byte read from FIFO. 
;	It is incremented before getting the next byte from the FIFO. 
 
; ----- put char in fifo -------------------------------------------- 
 
;	pass: HL = FIFO (destroyed) 
;		BC, DE destroyed 
 
PUT_FIFO: 
	LD	C,A		;put char in C 
	INC	(HL)		;incr the put offset 
	LD	A,(HL)		;get put offset 
	AND	FIFOSIZE-1	;mask it to range 
	INC	A		;add 1 for put offset, 1 for get offset 
	INC	A		;offset of first data byte 
	LD	E,A		;put in E 
	LD	D,0 
	ADD	HL,DE 
	LD	(HL),C		;store byte 
	RET			;all done 
 
; ----- get char from fifo ------------------------------------------ 
 
;	pass: HL = FIFO	(destroyed) 
;		DE destroyed 
;	return: A = char from fifo	 
 
GET_FIFO: 
	INC	HL		;point to get offset 
	INC	(HL)		;increment get offset 
	LD	A,(HL)		;get get offset 
	AND	FIFOSIZE-1	;mask to valid range 
	INC	A		;offset of first data byte (HL was incrd) 
	LD	E,A		;put in E 
	LD	D,0 
	ADD	HL,DE 
	LD	A,(HL)		;get byte 
	RET			;all done 
 
; ----- check FIFO empty -------------------------------------------- 
 
;	pass: HL = FIFO 
;	return: Z set if pointers match  
 
IS_FIFO_EMPTY: 
	INC	HL 
	LD	A,(HL)		;get get offset 
	DEC	HL 
	CP	(HL)		;compare to put offset 
	RET			;return 
 
; ----- are there characters waiting? ------------------------------- 
 
;int far inp_status( void ); 
 
; ?INP_STATUS::			;public entry point 
INP?STATUS:: 
	ADD	HL,SP 
	PUSH	HL 
 
	IF	POLLRECV 
 
	IN	A,(SIOACTRL) 
	BIT	RXRDY,A 
 
	ELSE 
 
	LD	HL,RECVFIFO 
	CALL	IS_FIFO_EMPTY 
 
	ENDIF 
 
	JR	Z,RET_0 
	LD	HL,1		;put 1 in return value 
;	JP	$RETI## 
	JP	$RTNI## 
 
RET_0: 
	LD	HL,0		;put 0 in return value 
;	JP	$RETI## 
	JP	$RTNI## 
 
; ----- output a character ------------------------------------------ 
 
;void far outp_char( char c ); 
 
;	char is on stack 
 
;?OUTP_CHAR:: 
OUTP?CHAR:: 
	ADD	HL,SP 
	PUSH	HL 
 
	LD	HL,4		;add 4 to SP 
	ADD	HL,SP 
	LD	C,(HL)		;get char passed to be sent (low order byte) 
 
	IF	POLLXMIT 
OLOOP: 
	IN	A,(SIOACTRL) 
	BIT	TXRDY,A 
	JR	Z,OLOOP 
	LD	A,C 
	OUT	(SIOADATA),A 
 
	ELSE 
 
	LD	A,(XMITTING)	;Check to see if transmitter is running 
	OR	A 
	JR	NZ,XC_0		;jump if it is. 
	INC	A 
	LD	(XMITTING),A	;set the flag 
	LD	A,C 
	OUT	(SIOADATA),A	;send to port, starting transmitter 
	JP	$RTNV## 
XC_0:				;transmitter is running, put in fifo 
	LD	HL,XMITFIFO 
	LD	A,C 
	CALL	PUT_FIFO	;put the char in the fifo 
 
	ENDIF 
 
	JP	$RTNV## 
 
;NOTE: Because there is no fifo full checking, the output fifo can be very 
;	easily overrun. For this reason, it may be desirable to use a POLLED 
;	output routine rather than an interrupt-driven one. 
 
; ----- SIO A transmit interrupt ------------------------------------ 
 
SA_XMIT_INT: 
	PUSH	HL 
	PUSH	DE 
	PUSH	BC 
	PUSH	AF 
 
	LD	HL,XMITFIFO 
 	CALL	IS_FIFO_EMPTY 
	JR	NZ,XI_NOT_2	;jump if not empty 
 
	LD	A,28H		;reset transmit interrupts 
	OUT	(SIOACTRL),A 
	XOR	A 
	LD	(XMITTING),A	;set flag, transmitter not running 
	JR	XI_RET 
 
XI_NOT_2: 
	CALL	GET_FIFO 
	OUT	(SIOADATA),A 
XI_RET: 
	POP	AF 
	POP	BC 
	POP	DE 
	POP	HL 
	EI 
	RETI 
 
; ----- SIO A receive interrupt ------------------------------------- 
 
SA_RECV_INT: 
	PUSH	HL 
	PUSH	DE 
	PUSH	BC 
	PUSH	AF 
 
	IN	A,(SIOADATA) 
	LD	HL,RECVFIFO 
	CALL	PUT_FIFO 
 
	POP	AF 
	POP	BC 
	POP	DE 
	POP	HL 
	EI 
	RETI 
 
; ----- flush all input characters ---------------------------------- 
 
;void far inp_flush( void ); 
 
;?INP_FLUSH::			;public entry point 
INP?FLUSH:: 
	ADD	HL,SP 
	PUSH	HL 
 
	IF	POLLRECV 
 
	IN	A,(SIOADATA) 
	IN	A,(SIOADATA) 
 
	ELSE 
 
	LD	HL,RECVFIFO 
	LD	A,(HL)		;get put pointer 
	INC	HL 
	LD	(HL),A		;and put at the get pointer 
 
	ENDIF 
 
;	LD	HL,0		;put 0 in return value 
;	JP	$RETI## 
	JP	$RTNV## 
 
; ----- deinit the communications port ------------------------------ 
 
;void far uninit_comm( void ); 
 
;?UNINIT_COMM::			;public entry point 
UNINIT?COMM:: 
	ADD	HL,SP 
	PUSH	HL 
 
	LD	A,018H		;channel reset 
	OUT	(SIOACTRL),A	;disable A port so it doesn't interrupt 
 
	JP	$RTNV## 
 
; ----- get an input character -------------------------------------- 
 
;char inp_char( void ); 
;NEEDS TO RETURN INT, NOT CHAR 
 
;?INP_CHAR::			;public entry point 
INP?CHAR:: 
	ADD	HL,SP 
	PUSH	HL 
 
	IF	POLLRECV 
 
ILOOP: 
	IN	A,(SIOACTRL) 
	BIT	RXRDY,A 
	JR	Z,RET_0_2 
	IN	A,(SIOADATA) 
 
	ELSE 
 
	LD	HL,RECVFIFO 
	CALL	IS_FIFO_EMPTY 
	JR	Z,RET_0_2 
	CALL	GET_FIFO 
 
	ENDIF 
 
;The value in the A register is demonstrably correct. Why the C program 
;is getting the wrong value, I have no idea. 
 
;	JP	$RETVAL## 
;RET_0_2: 
;	XOR	A 
;	JP	$RETVAL## 
 
	LD	L,A		;put char in return value 
	LD	H,0 
;	JP	$RETI## 
	JP	$RTNI## 
 
RET_0_2: 
	LD	HL,0		;put 0 in return value 
;	JP	$RETI## 
	JP	$RTNI## 
 
; ===== DATA AREAS ================================================== 
 
	DSEG 
 
XMITTING: DEFB	0 
 
	IF	KAYPRO 
SIOVEC:	DEFS	66		;8 vectors plus room to cross page boundary 
	ENDIF 
 
RECVFIFO: 
	DEFB	0,0		;get, put pointers 
	DEFS	FIFOSIZE 
XMITFIFO: 
	DEFB	0,0		;get, put pointers 
	DEFS	FIFOSIZE 
 
	END