TITLE HDLSIO - Z80 SIO HDLC DRIVER ROUTINES ; Driver routines to operate a port of a Zilog Z80-SIO ; in the SDLC/HDLC mode for packet radio operations. These ; routines operate in the half-duplex mode. The SIO RTS ; line is used to turn the transmitter on. Transmission ; of data will not begin until the SIO CTS line is asserted. ; ; Interface to the main program is via subroutine calls. ; Each call must be made with the address of the SIO data ; structure in the IY register. This data structure is ; as follows: INCLUDE STRUCT.LIB ; Upon activation, the main program should call the HDLINT ; initialization entry point to set up the SIO hardware. ; It is only necessary to call this routine once per ; SIO channel used. This routine is called only after a ; hardware reset as the routines will take care of all ; further SIO setup and disabling for the proper mode (Tx ; or Rx). When frame data is received, the external sub- ; routine NEXTHI will be called to accept each data byte ; or to flag the end of frame. When transmission is ; desired, the TXSKED entry point must be called to ; schedule the transmission. As each data byte is ; required by the SIO transmitter, the external sub- ; routine NEXTHO will be called. When this subroutine ; indicates the end of the data for the current transmission, ; the transmission will be terminated and the SIO placed ; back in receive mode. The external subroutine HODONE will ; then be called. PAGE ; All routines must be entered with the data structure address ; in IY. IX and IY registers are not affected by any of these ; routines. All external subroutines should return with IY ; unchanged. ; ; ENTRY POINTS: ; ; HDLI - Initialization subroutine. The SIO registers ; will be initialized in the receive mode. CDS ; will be called. ; Entry: No parameters. ; Return: All registers undefined. ; ; XSKED - Schedule transmission. ; Entry: A = 0 if CW id only ; = 1 if frame data to transmit ; Return: all registers undefined ; ; CDS - Carrier detect status reporting. ; Entry: No parameters required ; Return: A = 0 if DCD off ; A = 0FFH if DCD on ; EXTERNAL ROUTINES ; ; All of these routines are called within interrupt service. ; External routines may destroy any registers except IY ; and those specified below with return parameters. ; ; NXTHO - Routine to supply next data byte for trans- ; mission. ; Entry: No parameters ; Return: A = 0FFH if next data byte ; = 0 if end of message (more to follow) ; = 1 if end of transmission (no CW id) ; = 2 if end of transmission and id request ; B = next data byte (if A = 0FFH) ; ; ODONE - Routine at end of transmission. Called after ; receive mode is re-entered. No parameters. ; ; NXTHI - Routine to accept received data. All data ; after a call with A = 0 is invalid if another ; call with A = 0 occurs before a call with ; A = 1. ; Entry: A = 0FFH if next data byte ; A = 0 if start of frame ; A = 1 if end of frame ; B = next data byte (if A = 0 or 0FFH) ; Return: No parameters PAGE .Z80 ; SIO constants ;COMMANDS: RESTXI EQU 28H ;Reset TX interrupt RESEXT EQU 10H ;Reset external interrupt RESTCR EQU 80H ;Reset TX CRC generator RESEOM EQU 0C0H ;Reset TX end of message RESERR EQU 30H ;Reset error status ;STATUS BITS AND MASKS, RR 0 ABORT EQU 7 ;Break/abort MABORT EQU 80H EOM EQU 6 ;TX underrun/EOM MEOM EQU 40H CTS EQU 5 ;Clear-to-send line MCTS EQU 20H DCD EQU 3 ;Data-carrier-detect line MDCD EQU 8 TXBE EQU 2 ;TX buffer empty MTXBE EQU 4 RXBF EQU 0 ;RX buffer full MRXBF EQU 1 ;STATUS BITS AND MASKS, RR 1 EOF EQU 7 ;End of SDLC frame MEOF EQU 80H FERR EQU 6 ;Framing error MFERR EQU 40H OVR EQU 5 ;RX overrun MOVR EQU 20H PERR EQU 4 ;Parity error MPERR EQU 10H MRES EQU 0EH ;Residue mask ALL EQU 0 ;All sent MALL EQU 1 RES8 EQU 0110B ;Byte boundry residue ENTRY HDLI,XSKED ENTRY CDS ENTRY I.TBE,I.RBF,I.EXT,I.SRX EXT SETTMR SUBTTL 'Callable Subroutines' PAGE ; HDLI - SIO initialization routine HDLI: LD HL,SIOBI ;SIO init data LD B,SIOBIL ;Length of same LD C,(IY+CPORT) ;SIO control port OTIR ;Init the SIO (rx mode) XOR A LD (IY+TSTA),A ;Tx mode off (state 0) LD (IY+RSTA),A ;Rx state 0 (hunt) RET ; XSKED - Schedule transmission, either frame or CWID only XSKED: DI LD (IY+TSTA),1 ;Tx state = 1 LD C,(IY+TXDLY) ;Get TXDLY value LD B,(IY+(TXDLY+1)) LD A,B ;Check for no delay OR C ;Well? JR Z,XSKED1 ;No delay, skip timer PUSH IY ;Get structure base addr POP DE ;Timer service argument LD HL,TSERVE ;Where timer should call LD A,(IY+CHAN) ;Get channel # CALL SETTMR ;Set the timer LD (IY+TIMRUN),1 ;Show timer running XSKED1: CALL RTSON ;Start modem up EI RET ; CDS - DCD line status reporting subroutine CDS: LD C,(IY+CPORT) ;SIO control port IN A,(C) ;Get ext status BIT DCD,A ;Test DCD line LD A,0 ;Assume off RET Z LD A,0FFH ;Flag it on RET SUBTTL 'Tx Buffer Empty Interrupt Service' PAGE ; Transmitter buffer empty interrupt service. ; Action is as follows: ; State 0 (idle transmitter) ; Spurious interrupt -- ignore it. ; State 1 (waiting for CTS/timer) ; Spurious interrupt -- ignore it. ; State 2 (transmitting data) ; Call NEXTHO for data byte. If NEXTHO ; returns data, send to SIO. Otherwise, ; stuff dummy data to SIO. If end of ; transmission, state = 3, else 4. ; State 3 (end of frame) ; Call STFRM to re-start transmission. ; State = 2 ; State 4 (first EOT fill byte) ; Send dummy data to SIO, state = 5. ; State 5 (second EOT fill byte) ; Send dummy data to SIO, state = 6. ; State 6 (end of transmission) ; Set RTS off, call HODONE to flag ; end of transmission, state = 0. I.TBE: LD A,(IY+TSTA) ;Get current state INC A DEC A ;Idle state (0)? JR Z,SPURTI ;Yes, ignore int. DEC A ;Waiting state (1)? JR NZ,TXS2 ;No SPURTI: LD C,(IY+CPORT) ;SIO Control port LD A,RESTXI ;Reset tx interrupt OUT (C),A RET TXS2: DEC A ;Transmitting state (2)? JR NZ,TXS3 ;No CALL NEXTHO ;Get next byte OR A ;Is it a data byte? JP M,TXBYTE ;Yes, send it LD C,(IY+CPORT) ;SIO control port LD A,RESTXI ;Reset the interrupt OUT (C),A LD (IY+TSTA),3 ;Assume end of frame RET Z ;NEXTHO says end of frame LD (IY+TSTA),4 ;NEXTHO says end of transmission RET TXBYTE: LD C,(IY+DPORT) ;SIO data port OUT (C),B ;Send data byte RET PAGE TXS3: DEC A ;Fill 1 state (3)? JR NZ,TXS4 ;No CALL STFRM ;Restart transmission RET TXS4: DEC A ;Fill 2 state (4)? JR NZ,TXS5 ;No LD (IY+TSTA),5 ;State = 5 LD B,0 ;Data for fill JR TXBYTE ;Send fill TXS5: DEC A ;Fill 2 state (5)? JR NZ,TXTERM ;No LD (IY+TSTA),6 LD B,0 JR TXBYTE TXTERM: LD (IY+TSTA),0 ;Show us stopped CALL RTSOFF ;Stop transmitting CALL HODONE ;Report finish RET SUBTTL 'External/Status Interrupt Service' PAGE ; External/status interrupt handler. Check to see if ; rx frame aborted or DCD lost. If so, kill rx frame ; by setting rx state to 0. Then, if the tx state is 0, ; return. Otherwise, tx is active, so we will ; check the TX underrun status. If underrun and tx state ; is 2, execute tx state 8 routine (terminataion). ; If no underrun error, vector through either the CTS true ; or CTS false state table, as appropriate. I.EXT: LD C,(IY+CPORT) ;SIO control port IN A,(C) ;Get status register LD B,RESEXT ;Reset external status OUT (C),B LD B,A ;Save status AND MABORT+MDCD ;Mask all but abort, DCD CP MDCD ;Status ok? CALL NZ,RXTERM ;Terminate rx frame LD A,B ;Get status back LD B,(IY+TSTA) ;Get tx state INC B ;Set to check DEC B ;Tx state = 0? RET Z ;Yes, nothing to do PUSH AF ;Save status BIT EOM,A ;Is underrun status? JR Z,I.XT10 ;No LD A,(IY+TSTA) ;Get tx state CP 2 ;State 2 (data xfer)? JR NZ,I.XT10 ;No POP AF ;Clean stack JP TXTERM ;Terminate xmsn I.XT10: POP AF ;Get status into A CHKTX: BIT CTS,A ;Is CTS on? RET Z ;No, nothing to do LD A,(IY+TIMRUN) ;Is timer running? OR A RET NZ ;Yes, can't do anything LD A,(IY+TSTA) ;Get tx state CP 1 ;Waiting for it? JR Z,STFRM ;Yes, start the frame RET SUBTTL 'Internal Tx Subroutines' PAGE ; STFRM - Starts transmission of frame. ; Call NEXTHO for data byte. If none, ensure that RTS ; is off and set tx state = 0. Otherwise, reset Tx ; CRC generator, send the data byte to the SIO and set ; tx state = 2. STFRM: LD C,(IY+CPORT) ;SIO control port LD A,RESTCR ;Reset TX CRC OUT (C),A CALL NEXTHO ;Get that byte LD (IY+TSTA),2 ;State = 2 LD C,(IY+DPORT) ;SIO data port OUT (C),B ;Send the data LD C,(IY+CPORT) ;SIO control port LD A,RESEOM ;Reset Tx EOM status OUT (C),A RET ; RTSON - Turns RTS on RTSON: LD C,(IY+CPORT) ;SIO control port LD A,5 ;Select WR 5 OUT (C),A LD A,0EBH ;Set RTS true OUT (C),A RET ; RTSOFF - Turn RTS off RTSOFF: LD C,(IY+CPORT) ;SIO control port LD A,5 ;Select WR 5 OUT (C),A LD A,0E9H ;Set RTS false OUT (C),A RET ; TSERVE - Timer service TSERVE: PUSH DE ;Get data structure base POP IY LD (IY+TIMRUN),0 ;Show timer stopped LD C,(IY+CPORT) ;SIO control register IN A,(C) ;Get status JP CHKTX ;Go check transmission SUBTTL 'Rx Buffer Full Interrupt Service' PAGE ; Receive buffer full interrupt routine. Get received ; char into B and exwcute based on receive state. I.RBF: LD C,(IY+DPORT) ;SIO data port IN B,(C) ;Get received data byte LD A,(IY+RSTA) ;Get rx state INC A DEC A ;Is rx state 0? JR NZ,RS1 ;No ; Rx State 0, the idle state, is maintained until the first ; receive interrupt. Due to the fact that the SIO passes ; FCS as data, a one byte buffer is used to insure that FCS ; never gets passed to NEXTHI. ; Store received char in buffer, state = 1 LD (IY+CBUF),B ;Buffer received char LD (IY+RSTA),1 ;Next state = 1 RET RS1: DEC A ;Is rx state 1? JR NZ,RS2 ; Rx states 1 and 2 - Place the received char in the ; buffer, sending the current buffer char to NEXTHI. ; Set state = 2 LD A,0 ;Flag SOM to NEXTHI JR GIVERX RS2: LD A,0FFH ;Flag next data byte to NEXTHI GIVERX: LD C,(IY+CBUF) ;Save buffer char LD (IY+CBUF),B ;Buffer received char LD B,C ;Get old buffer char CALL NEXTHI ;Give to upper level sub LD (IY+RSTA),2 ;Next state = 2 RET PAGE ; Special receive interrupt routine. Occurs on error ; or end of frame. If in state 2 and no error, ; execute state 3 routine (valid EOM). If error status, ; terminate frame. Possible errors: CRC error, rx ; underrun, invalid residue code (not 8-bit boundry) I.SRX: LD C,(IY+CPORT) ;SIO control port LD A,1 ;Set RR 1 OUT (C),A IN A,(C) ;Get special rx status LD B,A ;Save it LD A,RESERR ;Reset error status OUT (C),A LD C,(IY+DPORT) ;SIO data port IN A,(C) ;Get last FCS byte LD A,B ;Get status AND MEOF+MFERR+MOVR+MRES ;Save the important bits CP MEOF+RES8 ;Proper end of frame? JR NZ,RXTERM ;No, terminate frame LD A,(IY+RSTA) ;We in state 2? CP 2 LD A,1 ;NEXTHI final flag CALL Z,NEXTHI ;Tell NEXTHI is a good one RXTERM: LD (IY+RSTA),0 ;Reset to state 0 RET PAGE ; External subroutine calls NEXTHO: LD L,(IY+NXTHO) ;Get NXTHO address LD H,(IY+(NXTHO+1)) JP (HL) NEXTHI: LD L,(IY+NXTHI) ;Get NXTHI address LD H,(IY+(NXTHI+1)) JP (HL) HODONE: LD L,(IY+ODONE) ;Get ODONE address LD H,(IY+(ODONE+1)) JP (HL) ; Fixed data ; SIO initialization block. SIOBI: DB 18H ;Reset SIO DB 4 ;WR 4 DB 00100000B ;SDLC mode, x1 clock DB 5 ;WR 5 DB 11101001B ;DTR on, RTS off DB 3 ;WR 3 DB 11111001B ;8 bits, enables DB 7 ;WR 7 DB 01111110B ;SDLC flag byte DB 1 ;WR 1 DB 00011111B ;Enable rx, ext ints SIOBIL EQU $-SIOBI END