📄 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -