📄 xsio.as
字号:
;XSIO.AS - 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
; 941012 rr version for hi-tech c
;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 );
psect text ;Code segment
; Hi-Tech C pushes parameters on the stack, and return value goes in HL.
; Simple enough.
; Assembler bugs in Hi-Tech C: ELSE does not work. IF does not work, but
; you can use COND.
; ----- Conditionals ------------------------------------------------
XEROX820 EQU 1 ;if assembling for Xerox 820
KAYPRO EQU 0 ;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. The base (FF00-FF0F) of this
;vector is for the SIO.
;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 -----------------------------------------------------
COND XEROX820
SIO EQU 4 ;I/O port of Z-80 SIO/0
SIOVEC EQU 0FF00H ;interrupt vector for SIO (mode 2)
BAUDPORT EQU 0 ;baud rate port for channel A (0C = B)
B9600 EQU 0EH ;value for 9600 baud
ENDC
COND 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
ENDC
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 );
global _init_comm
_init_comm:
COND KAYPRO
DI ;disable ints til we have them set up
ENDC
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
COND 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)
ENDC
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
COND KAYPRO
IM 2 ;set interrupt mode 2
EI
ENDC
LD A,B9600
OUT (BAUDPORT),A ;set baud rate
XOR A
LD (XMITTING),A ;zero transmitting flag
LD HL,1 ;return 1
RET
; ----- 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
COND POLLRECV
COND POLLXMIT
DEFB 0H ;no interrupts
ENDC
COND POLLXMIT.EQ.0
DEFB 02H ;xmit interrupt only
ENDC
ENDC
COND POLLRECV.EQ.0
COND POLLXMIT
DEFB 18H ;recv interrupt only
ENDC
COND POLLXMIT.EQ.0
DEFB 1AH ;recv & xmit interrupt
ENDC
ENDC
;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 );
global _inp_status
_inp_status: ;public entry point
COND POLLRECV
IN A,(SIOACTRL)
BIT RXRDY,A
ENDC
COND POLLRECV.EQ.0
LD HL,RECVFIFO
CALL IS_FIFO_EMPTY
ENDC
JR Z,RET_0
LD HL,1 ;put 1 in return value
RET
RET_0:
LD HL,0 ;put 0 in return value
RET
; ----- output a character ------------------------------------------
;void far outp_char( char c );
; char is on stack
global _outp_char
_outp_char:
LD HL,2 ;add 2 to SP
ADD HL,SP
LD C,(HL) ;get char passed to be sent (low order byte)
COND POLLXMIT
OLOOP:
IN A,(SIOACTRL)
BIT TXRDY,A
JR Z,OLOOP
LD A,C
OUT (SIOADATA),A
ENDC
COND POLLXMIT.EQ.0
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
RET
XC_0: ;transmitter is running, put in fifo
LD HL,XMITFIFO
LD A,C
CALL PUT_FIFO ;put the char in the fifo
ENDC
RET ;return
;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 );
global _inp_flush
_inp_flush:
COND POLLRECV
IN A,(SIOADATA)
IN A,(SIOADATA)
ENDC
COND POLLRECV.EQ.0
LD HL,RECVFIFO
LD A,(HL) ;get put pointer
INC HL
LD (HL),A ;and put at the get pointer
ENDC
LD HL,0 ;put 0 in return value
RET
; ----- deinit the communications port ------------------------------
;void far uninit_comm( void );
global _uninit_comm
_uninit_comm:
LD A,018H ;channel reset
OUT (SIOACTRL),A ;disable A port so it doesn't interrupt
RET
; ----- get an input character --------------------------------------
;int inp_char( void );
global _inp_char
_inp_char:
COND POLLRECV
ILOOP:
IN A,(SIOACTRL)
BIT RXRDY,A
JR Z,RET_0_2
IN A,(SIOADATA)
ENDC
COND POLLRECV.EQ.0
LD HL,RECVFIFO
CALL IS_FIFO_EMPTY
JR Z,RET_0_2
CALL GET_FIFO
ENDC
LD L,A ;put char in return value
LD H,0
RET
RET_0_2:
LD HL,0 ;put 0 in return value
RET
; ===== DATA AREAS ==================================================
psect data
XMITTING: DEFB 0
COND KAYPRO
SIOVEC: DEFS 66 ;8 vectors plus room to cross page boundary
ENDC
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 + -