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