{ File: [Iris]<WMicro>DLion>RS232CInterrupts.asm
Modification History:
Dennis Grundler: 1-Sep-84 17:10:49 Add copyright notice
Mike Thatcher: 29-Aug-84 22:49:50 fix to output Xon/Xoff after receiving an Xoff
Mike Thatcher: 23-Aug-84 18:14:10 fixed TxBufferPointer check after Xon
Mike Thatcher: 21-Aug-84 17:14:16 add EI in Xoff1 interrupt
Mike Thatcher: 13-Aug-84 6:22:54 Fix for saving completion code in RxAsyncSpecialInt and CheckFifoBoundary
Mike Thatcher: 27-Mar-84 9:49:41 Fix to flow control glitch
Mike Thatcher: 13-Jan-84 14:26:02 Incorporate Grundler's fixes for aborting when FifoOverflow and EXPort OverflowFlag.
Mike Thatcher: 3-Jan-84 15:03:49 Added Async Flow Control
Tom Chang : 17-May-83 14:44:40 Make sure only use 3 SIO interrupt bits
Chuck Fay : 17-Nov-82 18:10:24: Fix FifoOverflow handling; move all stack POPs
before EIs to avoid potential stack overflow memory smash; change RxIllegalInt
and TxIllegalInt to press on rather than crash with MP 515.
Jim Frandeen : September 17, 1982 8:51 AM: Allow any block size in Async mode.
Jim Frandeen : May 25, 1982 3:56 PM
Jim Frandeen : April 13, 1982 11:32 AM: Created file.
}
{ Copyright (C) 1982, 1983, 1984 by Xerox Corporation. All rights reserved.}
Get "SysDefs"
Get "RS232CDefs"
Get "CommonDefs"
IMP AsyncTimerCounter ;From SIOSubs
IMP AsyncTimerStoppedFlag ;From SIOSubs
IMP GetByteCount ;From RS232CGet
IMP ErrorReport ;From Common
IMP RxFIFO ;From Buffer
IMP RxMaxFrameSize ;From BisyncInput
IMP StatusChangeFlag ;From RS232CGet
IMP TxBisyncStateSwitch ;From BisyncInterrupts
IMP TxUnderrunDetectedFlag ;From RS232CPut
EXP AsyncRS232CInterrupt
EXP AsyncTimerValue
EXP CharactersLeftInClientBuffer
EXP CharactersUntilBoundary
EXP CharLengthMask
EXP CharLengthMask1
EXP PrevAsyncDCD
EXP PrevCTS
EXP PrevSdlcDCD
EXP CurrentFrameToFillPtr
EXP CurrentFrameToSendPtr
EXP EndOfFrame
EXP ExitInterrupt
EXP FCState
EXP FifoStateSwitch
EXP FirstFrameToFillPtr
EXP FirstRxCharPtr
EXP FirstMaxFrameBoundary
EXP FlowControlFlag
EXP LastBufferFlag
EXP LastNewFrameAddressHigh
EXP LastNewFrameAddressLow
EXP LastTxBufferAddressHigh
EXP LastTxBufferAddressHigh1
EXP LastTxBufferAddressLow
EXP LastTxBufferAddressLow1
EXP MaxFrameSize
EXP NoMoreBytesToSend
EXP OutputXoff
EXP OutputXon
EXP OverflowFlag
EXP PutCompletedFlag
EXP PutFinishedSwitch
EXP RxAsyncDataInt
EXP RxAsyncExternalInt
EXP RxAsyncSpecialInt
EXP RxFifoState
EXP RxFifoEnd
EXP RxIllegalInt
EXP RxSdlcDataInt
EXP RxSdlcExternalInt
EXP RxSdlcSpecialInt
EXP SetLastBufferFlag
EXP StoreFrameHeader
EXP TxAsyncDataInt
EXP TxAsyncExternalInt
EXP TxBisyncExternalInt
EXP TxBufferPointer
EXP TxSdlcExternalInt
EXP SdlcFrameFlag
EXP SdlcRS232CInterrupt
EXP TerminateFrame
EXP TxIllegalInt
EXP XoffReceivedFlag
EXP XoffChar
EXP XonChar
;
{This is what a frame looks like:}
FrameKey:
EQU 0 ;# FrameValid => frame has been stored over
FrameReadyFlag:
EQU 1 ;#0 => frame is ready to send
FrameLastCharLow:
EQU 2
FrameLastCharHigh:
EQU 3 ;stored by interrupt routine
FrameCPAddrLow:
EQU 4 ;low byte of CP address
FrameCPAddrMid:
EQU 5 ;middle byte of CP address
FrameCPAddrHigh:
EQU 6 ;high byte of CP address
FrameSizeLow:
EQU 7 ;number of data bytes in frame
FrameSizeHigh:
EQU 8
FrameCommand:
EQU 9 ;WriteBlock command for CP micrrocode
FrameData:
EQU 10 ;First character of data
FrameValid EQU 7EH
;
AsyncRS232CInterrupt:
{ Come here in Async mode from Common when we get a RST 6.5 Interrupt from the Zilog SIO chip. Save state }
PUSH PSW
PUSH H
{Read the interruptVector to determine the cause of the interrupt. Multiply the vector by 2. This gives us a number which we can OR into the low bits of the jump table address. The jump table is in the Common so that we can assure its exact address and guarantee that the low 5 bits of the address are zero. }
MVI A,PointToWR2
OUT TxCont
IN TxCont ; Read Interrupt Vector
{added the following}
ANI 0EH ; just allow rightful three bits, 5/17/83
RLC ; Interupt Vector * 2
ORI 60H ; Low address is 60H
MOV L,A
MVI H,20H ;High address is 20H
PCHL ;Jump to JumpTable
{AsyncJumpTable: @2060H
JMP TxAsyncDataInt ; When Vecter =0 Tx Buffer Empty
DB 0
JMP TxAsyncExternalInt ; When Vecter =2 Ex Stat
DB 0
JMP TxIllegalInt ; When Vecter =4 Rx Char
DB 0
JMP ExitInterrupt ; When Vecter =6 Sp Rx Cond
DB 0
JMP RxIllegalInt ; When Vecter =8 Tx Buffer Empty
DB 0
JMP RxAsyncExternalInt ; When Vecter =A Ex Stat
DB 0
JMP RxAsyncDataInt ; When Vecter =C Rx Char
DB 0
JMP RxAsyncSpecialInt ; When Vecter =E Sp Rx Cond
}
{The following interrupts should never happen, but occasionally they do, perhaps because of crosstalk on the DLion RS232 cable. Therefore, we just ignore them, rather than crash with an MP code. The one that seems to happen is RxIllegalInt. The counters are for debugging use, for instance of new cables.}
RxIllegalInt:
LXI H,RxIllegalIntCount
INR M ;Increment illegal interrupt counter
JMP ExitInterrupt
TxIllegalInt:
LXI H,TxIllegalIntCount
INR M ;Increment illegal interrupt counter
JMP ExitInterrupt
RxIllegalIntCount:
DB 0
TxIllegalIntCount:
DB 0
;
TxAsyncDataInt:
{Come here when we get an interrupt for Tx Buffer Empty. See if the last character has already been sent.}
DB opMVIA ;A ← XoffReceivedFlag
XoffReceivedFlag:
DB 0
ORA A ;IF XoffReceivedFlag
JNZ ResetTxInterrupt ;THEN stop sending data
TestTxEnd:
LHLD TxBufferPointer ;ELSE IF ~lastCharacter sent
DCX H
LDA LastTxBufferAddressLow
CMP L
JNZ SendNextAsyncCharacter
LDA LastTxBufferAddressHigh
CMP H
JNZ SendNextAsyncCharacter ; THEN send next character
PutCompleted:
{We already sent the last character.}
MVI A,ResetTxIntPending ;ELSE putCompleted
OUT TxCont
STA PutCompletedFlag
POP H
POP PSW
EI ;Enable interrupts
RET
ResetTxInterrupt;
LDA FCState
ORI StoppedTx
STA FCState
MVI A,ResetTxIntPending
OUT TxCont
POP H
POP PSW
EI ;Enable interrupts
RET
SendNextAsyncCharacter:
INX H
MOV A,M ;Get byte from buffer
OUT TxData ;Send byte
INX H ;Point to next character
SHLD TxBufferPointer ;Update buffer pointer
POP H
POP PSW
EI ;Enable interrupts
RET
;
RxAsyncDataInt:
{ Come here when the SIO chip has another character of data ready. Reset the Async timer.}
DB opLXIH ;HL ← AsyncTimerValue
AsyncTimerValue:
DW 0
SHLD AsyncTimerCounter ;Set iteration counter
MVI A,0B0H ;timer channel 2, mode 0, binary
OUT TimerMode
LXI H,TimerCount2+8000H
MVI M,0FEH ;Send low byte of 1840 decimal
MVI M,47H ;Send high byte
XRA A
STA AsyncTimerStoppedFlag
{Fetch the next character and mask it, depending on the length of the character. Store the character, increment the pointer to the next character position.}
IN RxData ;A ← RxData
DB opANI ;A ← RxData AND CharLengthMask
CharLengthMask:
DB 0
STAX B ;Store character in Fifo
INX B
{Check for flow control, if true look for receiving an xoff/xon character and set appropriate flags. If no xon/xoff charater then determine if high water mark has been reached in the buffer. Because of the buffering scheme, the high water mark(HWM) is set to MaxFrameSize + 138. This is not looked for until there is less then 2*MaxFrameSize left in the buffer. The LastBufferFlag is set when this state has occured}
MOV L,A ;Temporary save character
LDA FlowControlFlag ;IF ~FlowControl
ORA A
JZ CheckClientSpace ;go check space left in client's buffer
MOV A,L ;ELSE restore received character
DB opLXIH ;H←XonChar, L←XoffChar
XoffChar:
DB 0
XonChar:
DB 0
CMP L ; IF ~Xoff Char THEN
JNZ TestXon ; go test for Xon
DCX B ; ELSE
STA XoffReceivedFlag ;set XoffReceived Flag
JMP Out ; return
TestXon:
CMP H ;IF ~Xon Char THEN
JNZ TestSpaceLeft ; test space left
DCX B ;ELSE
XRA A ; reset XoffReceivedFlag
STA XoffReceivedFlag
LDA PutCompletedFlag
ORA A ;IF ~PutCompleted
JNZ Out
SendChar:
LDA FCState
ANI -StoppedTx-1
STA FCState
JMP TestTxEnd
TestSpaceLeft:
PUSH D
CALL CheckFifoBoundary
POP D
CheckClientSpace: ;ELSE Null
{See if we are have filled the Iocb.}
DB opLXIH ;HL ← CharactersUntilBoundary
CharactersLeftInClientBuffer:
DW 0
DCX H ;Decrement count
MOV A,H
ORA L
JZ ClientBufferFull
SHLD CharactersLeftInClientBuffer
{See if we are at the end of a block.}
DB opLXIH ;HL ← CharactersUntilBoundary
CharactersUntilBoundary:
DW 0
DCX H ;Decrement boundary count
MOV A,H
ORA L
JZ BlockFull
SHLD CharactersUntilBoundary
Out:
POP H
POP PSW
EI ;Enable interrupts
RET
ClientBufferFull:
{Come here if this character has filled the client's buffer. End this frame, mark it EOF, and start a new frame. This is a normal completion for Asynchronous mode. In asynchronous mode, a frame ends either when it fills the IOCB or when we get a timeout, whichever comes first.}
PUSH D ;Save DE
MVI E,80H ;Set EndOfFrame
JMP TerminateFrame
BlockFull:
{Come here if this character has filled the block. This happens when a client's buffer is larger than our input buffer. End this frame, mark it end of block, and start a new frame. The Get routine will send this data to the CP.}
PUSH D ;Save DE
MVI E,4 ;Set EndOfBlock
JMP TerminateBlock
;
RxAsyncExternalInt:
{ Come here when we get an External/Status interrupt. Register RR0 indicates the cause of the interrupt:
80: 10000000 Break Asynchronous
08: 00001000 Data Carrier Detect (DCD)}
IN RxCont ;Read RR0
MOV H,A ;Save RR0 in H
ANI DCD
DB opCPI ;Compare to previous DCD
PrevAsyncDCD:
DB 0
STA PrevAsyncDCD ;Save current DCD
MVI A,ResetExternalStatusInterrupts
OUT RxCont
JZ TestRxAsyncBreak
{Continue if DataCarrierDetect has changed state. Notify the Get loop so that it will do a NakedNotify. We store ResetExternalStatusInterrupts in StatusChangeFlag.}
STA StatusChangeFlag
TestRxAsyncBreak:
ORA H ;RR0 will be negative if Abort bit is set
JM AsyncBreakAbortDetected
POP H
POP PSW
EI ;Exit Interrupt
RET
AsyncBreakAbortDetected:
{We got a Break/Abort. We end the current frame and return it to the CP with aborted status in the Iocb. Set FrameCompletionCode to Aborted. NOTE: If this frame does not contain any characters, we will not send any. We check for this at StartFrameTransfer.}
PUSH D ;Save DE
MVI E,1 ;Set CompletionCode to Aborted
TerminateFrame:
{We get called from Get when we get a timeout or an abort. E = CompletionCode: 1 for Aborted or 80H for Timeout. End this frame and start a new one.}
LHLD GetByteCount
SHLD CharactersLeftInClientBuffer
TerminateBlock:
{RxMaxFrameSize is the number of characters until our next boundary.}
LHLD RxMaxFrameSize
SHLD CharactersUntilBoundary
JMP EndOfFrame
;
RxAsyncSpecialInt:
{ Come here when we get a Special interrupt. Register RR1 indicates the cause of the interrupt:
40: 01000000 Framing error in Async
20: 00100000 RxOverrun
10: 00010000 Parity error
A Framing Error occurs if the character is assembled without any stop bits. This bit is set only for the character on which it occurred.
RxOverrun means data is being overwritten because the channel's three-byte receiver buffer is full and a new character is being received.
A Parity Error occurs when the parity bit of the character does not match with the programmed parity. Once this bit is set, it remains set until the Error Reset Command is given.}
PUSH D ;Save DE
{Read the character that caused the interrupt and place it in the RxFifo as we would a normal character.}
IN RxData ;A ← RxData
DB opANI ;A ← RxData AND CharLengthMask
CharLengthMask1:
DB 0
STAX B ;Store character in Fifo
INX B
LXI H,8000H+RxCont ;Point to RxCont register
MVI M,PointToWR1
MOV A,M ;Read CompletionCode from RR1
ANI 0F0H
MOV E,A ;Save CompletionCode in E
MVI M,ErrorReset
{RxMaxFrameSize is the number of characters until our next boundary.}
LHLD RxMaxFrameSize
SHLD CharactersUntilBoundary
{Check if need to send Xon/Xoff}
LDA FlowControlFlag ;IF ~FlowControl
ORA A
JZ EndOfFrame
PUSH D ;Save completion code
CALL CheckFifoBoundary
POP D ;restore completion code
JMP EndOfFrame
;
SdlcRS232CInterrupt:
{ Come here in Sdlc mode from the EXEC when we get a RST 6.5 Interrupt from the Zilog SIO chip. Save state }
PUSH PSW
PUSH H
{Read the interruptVector to determine the cause of the interrupt. Multiply the interrupt vector by 2. This gives us a number which we can OR into the low bits of the jump table address. The jump table is in the Common so that we can assure its exact address and guarantee that the low 5 bits of the address are zero.}
MVI A,PointToWR2
OUT TxCont
IN TxCont ; Read Interrupt Vector
{removed ORA A, replaced by the following}
ANI 0EH ; just allow rightful three bits, 5/17/83
PutFinishedSwitch:
{This opcode will be changed to a JMP when the last character has been sent. The jump table will then pass control to NoMoreBytesToSend.}
DB opJNZ
DW BranchOnSdlcInterruptVector
TxSdlcDataInt:
{Come here when we get an interrupt for Tx Buffer Empty. Send another character to the SIO chip.}
DB opLXIH ;HL ← TxBufferPointer
TxBufferPointer:
DW 0
{We want to send the character and then test for last so as to service the interrupt as fast as possible.}
MOV A,M ; get byte from buffer
OUT TxData ; send byte
DB opMVIA ;A ← LastTxBufferAddressLow
LastTxBufferAddressLow:
DB 0
CMP L
JZ LastTxBufferAddressLowEqual
INX H ; increment buffer pointer
SHLD TxBufferPointer ; update buffer pointer
ExitInterrupt:
{Come here to restore state and exit. }
POP H
POP PSW
EI ;Enable interrupts
RET
LastTxBufferAddressLowEqual:
DB opMVIA ;A ← LastTxBufferAddressHigh
LastTxBufferAddressHigh:
DB 0
CMP H
JZ LastByteToSend
INX H ; increment buffer pointer
SHLD TxBufferPointer ; update buffer pointer
POP H
POP PSW
EI ;Enable interrupts
RET
LastByteToSend:
{We have just sent the last byte. The next time we get a TxData interrupt, we will JMP to NoMoreBytesToSend.}
MVI A,opJMP
STA PutFinishedSwitch
STA PutCompletedFlag
POP H
POP PSW
EI ;Enable interrupts
RET
NoMoreBytesToSend:
MVI A,ResetTxIntPending
OUT TxCont ; disable this interrupt & exit
POP H
POP PSW
EI ;Enable interrupts
RET
;
BranchOnSdlcInterruptVector:
RLC ;Interupt Vector * 2
MVI H,20H ;High address is 20H
ORA H ;Low address is 20H
MOV L,A
PCHL ;Jump to JumpTable
{SdlcJumpTable: @2060H
JMP NoMoreBytesToSend ; When Vecter =0 Tx Buffer Empty
DB 0
JMP TxSdlcExternalInt ; When Vecter =2 Ex Stat
DB 0
JMP TxIllegalInt ; When Vecter =4 Rx Char
DB 0
JMP ExitInterrupt ; When Vecter =6 Sp Rx Cond
DB 0
JMP RxIllegalInt ; When Vecter =8 Tx Buffer Empty
DB 0
JMP RxSdlcExternalInt ; When Vecter =A Ex Stat
DB 0
JMP RxSdlcDataInt ; When Vecter =C Rx Char
DB 0
JMP RxSdlcSpecialInt ; When Vecter =E Sp Rx Cond
}
;
TxSdlcExternalInt:
TxAsyncExternalInt:
TxBisyncExternalInt:
{Tx Sdlc External Interrupt processing routine
Come here if we get Break, Clear To Send, Data Carrier Detect, or Tx Underrun. Notify the Sdlc Put Loop if we get underrun.}
IN TxCont
MOV H,A ;Save RR0 in H
ANI TxUnderrun
STA TxUnderrunDetectedFlag
{Test for change in Clear To Send.}
MOV A,H
ANI CTS
DB opCPI ;Compare to previous CTS
PrevCTS:
DB 0
STA PrevCTS ;Save current CTS
MVI A,ResetExtStatusInterrupts
OUT TxCont
JZ NoChangeInCTS
STA StatusChangeFlag
NoChangeInCTS:
POP H
POP PSW
EI ;Enable interrupts
RET
;
RxSdlcDataInt:
{Well here it is folks! This is what we have all been waiting for. This is why Domino never uses registers B and C. They are reserved for NextRxCharPtr so that SDLC will run at 56KB in interrupt mode.
Fetch the next character and store the character, increment the pointer to the next character position. Note that we do not check for any boundary. We depend on the EndOfFrame interrupt to stop the receiver. At 56KB, we don't have time to check the boundary.}
{Set SdlcFrameFlag so we know we're in a frame if we get an abort. A = 18H from the InterruptVector*2. This seems to waste 13 cycles, but Abort is the longest interrupt, and it will cause Overruns if we try a longer method of finding out if we are in a frame.}
STA SdlcFrameFlag
IN RxData ;A ← RxData
STAX B ;Store character in Fifo
INX B
POP H
POP PSW ; Restore Registers
EI
RET
;
RxSdlcExternalInt:
{ Come here when we get an External/Status interrupt. Register RR0 indicates the cause of the interrupt:
80: 10000000 Break
08: 00001000 Data Carrier Detect (DCD)}
IN RxCont ;Read RR0
MOV H,A ;Save RR0 in H
ORA H
JP TestSdlcStateChange
{We got a Break/Abort. Check to see if we are processing a frame. Breaks detected when the SIO is not synchronized to the data stream are not interesting.}
DB opMVIA ;A ← SdlcFrameFlag
SdlcFrameFlag:
DB 0
ORA A
JZ TestSdlcStateChange
{If we are in a frame, we end the current frame and return it to the CP with aborted status in the Iocb. Set FrameCompletionCode to Aborted. NOTE: If this frame does not contain any characters, we will not send any. We check for this at StartFrameTransfer.}
PUSH D ;Save DE
MVI E,1 ;Set CompletionCode to Aborted
JMP EndOfFrame
TestSdlcStateChange:
MOV A,H ;A ← RR0
ANI DCD
DB opCPI ;Compare to previous DCD
PrevSdlcDCD:
DB 0
STA PrevSdlcDCD ;Save current DCD
MVI A,ResetExternalStatusInterrupts
OUT RxCont
JZ ExitSdlcExternalInt
{DataCarrierDetect has changed state. Notify the Get loop so that it will do a NakedNotify. We store ResetExternalStatusInterrupts in StatusChangeFlag.}
STA StatusChangeFlag
ExitSdlcExternalInt:
POP H
POP PSW
EI ;Exit Interrupt
RET
;
RxSdlcSpecialInt:
{ Come here when we get a Special interrupt. Register RR1 indicates the cause of the interrupt:
80: 10000000 End Of Frame
40: 01000000 CRC error
20: 00100000 RxOverrun
10: 00010000 Parity error
RxOverrun means data is being overwritten because the channel's three-byte receiver buffer is full and a new character is being received.
A Parity Error occurs when the parity bit of the character does not match with the programmed parity. Once this bit is set, it remains set until the Error Reset Command is given.}
PUSH D ;Save DE
{Read the character that caused the interrupt and place it in the RxFifo as we would a normal character.}
IN RxData ;A ← RxData
STAX B ;Store character in Fifo
INX B
LXI H,8000H+RxCont ;Point to RxCont register
MVI M,PointToWR1
MOV A,M ;Read CompletionCode from RR1
ANI 0F0H ;Mask off CRC residue
MOV E,A ;Save CompletionCode in E
MVI M,ErrorReset
;
{This is the Get Fifo processing system. The interrupt routine puts characters into the Fifo, and the Get routine removes one frame at a time. Each time a frame is ready, the Fifo routine notifies the Get routine by posting a completion code in the frame header. The interrupt routine fills a frame and sets the FrameCompletionFlag # 0 when a frame is ready to send. The interrupt routine stores the address of the last character stored + 1 in FrameLastChar.
We never start a new frame unless there is room in the Fifo for the maximum frame size + the size of the frame header. RxMaxFrameSize contains the maximum frame size. We get an overflow when there is no room in the Fifo for a new frame.
The first frame begins at RxFifo. The next frame begins at the next address following the first frame. If the following calculation is negative, we need to start a new frame at the beginning of the RxFifo: LastNewFrameAddress - NextFrameToFillPtr.}
{The Fifo has two states: Open and Closed. The Fifo is in the Open state when its boundary is the end of the Fifo. The Fifo is in the Closed state when its boundary is the next frame to send. In the Closed state, we have to be careful not to run into the next frame to send. It is in the Closed state that we are in danger of overflow. An overflow condition occurs when there is no room for a maximum size frame.
The Fifo starts in the Open state. We pass from the Open state to the Closed state when the interrupt routine wraps around and begins a new frame to fill at the beginning of the Fifo. We pass from the Closed state to the Open state when the Get routine wraps around and begins a new frame to send at the beginning of the Fifo.}
EndOfFrame:
{Come here when a frame has terminated. E = CompletionCode. BC points to the last character stored + 1. We have satisfied the Rx interrupt condition. Since this is EndOfFrame, we won't have to worry about another input character for a while, but we do have to worry about the transmit buffer becoming empty. This is a good time to check for it.}
MVI A,PointToWR2
OUT TxCont
IN TxCont ; Read Interrupt Vector
ORA A
JZ HandleTxDataInEOF
StoreFrameHeader:
{Enter here for Bisync end of frame.}
DB opLXIH ;HL ← CurrentFrameToFillPtr
CurrentFrameToFillPtr:
DW 0 ;HL points to CompletionCode
{The CompletionCode is in E. OR this with the fifo OverflowFlag in case this frame already had a fifo overflow.}
DB opMVIA ;A ← OverflowFlag
OverflowFlag:
DB 0 ;0 => no overflow, 2 => overflow
ORA E
MOV M,A ;Store CompletionCode
XRA A ;WARNING! Note side effect on FifoStateSwitch below
STA OverflowFlag ;Turn off fifo overflow flag for next time
INX H ;Point to count field of Frame
MOV M,C ;Store low pointer to last char
INX H ;Point to count field high of frame
MOV M,B ;Store high pointer to last char
{Now initialize the NextFrameToFill depending on the state of the Fifo: Open or Closed. The following op code will be JMP if the Fifo State is Closed, or JNZ if the Fifo State is Open. Because of the XRA instruction above, the ConditionCode will always be = 0 when we come here. When the Fifo wraps around in the Get routine, we set the Switch to opJNZ for Open. When the Fifo wraps around in the interrupt routine, we set the Switch to opJMP for Closed.}
FifoStateSwitch:
DB opJNZ
DW FifoClosed
FifoOpen:
{JNZ will always fall through when the State is Open. Come here if the next frame to send is behind us. This means we have until the end of the Fifo for another frame. See if we have enough room in the Fifo to start another frame. If the following calculation is negative, we have to start the next frame at the beginning of the Fifo: LastNewFrameAddress - NextFrameToFillPtr.}
DB opMVIA ;A ← LastNewFrameAddressLow
LastNewFrameAddressLow:
DB 0
SUB C
DB opMVIA ;A ← LastNewFrameAddressHigh
LastNewFrameAddressHigh:
DB 0
SBB B
JM StartFirstAsyncFrame
{Continue if we have room for another frame. Now we can initialize CurrentFrameToFillPtr and BC (NextRxCharPtr). Set HL to point to new frame.}
MOV D,B
MOV E,C ;DE ← CurrentFrameToFillPtr
LXI H,FrameData
DAD D ;HL ← new NextRxCharPtr
XCHG ;HL ← CurrentFrameToFillPtr
MOV B,D
MOV C,E ;BC ← NextRxCharPtr
MVI M,FrameValid ;Set FrameValid
INX H
SHLD CurrentFrameToFillPtr
XRA A
MOV M,A ;Turn off CompletionCode
STA SdlcFrameFlag
DB opMVIA ;A ← FlowControlFlag
FlowControlFlag:
DB 0
ORA A
CNZ SetLastBufferFlag
POP D ;Restore DE
POP H
POP PSW
EI ;Enable interrupts
RET
StartFirstAsyncFrame:
{Come here to begin a new frame at the beginning of the Fifo. First, be sure we have room to start a new frame at the beginning of the Fifo. We calculate: CurrentFrameToSendPtr - (FirstFrameToFillPtr + MaxFrameSize). If this is negative, we have an overflow condition.}
LHLD CurrentFrameToSendPtr
DB opLXID ;DE ← FirstMaxFrameBoundary
FirstMaxFrameBoundary:
DW 0
MOV A,L
SBB E
MOV A,H
SBB D
JM FifoOverflow
{Continue if we have room to start a new frame. Now we can initialize the FifoState to Closed.}
MVI A,opJMP
STA FifoStateSwitch
STA RxFifoState
DB opLXIB ;BC ← FirstRxCharPtr
FirstRxCharPtr:
DW 0
DB opLXIH ;HL ← FirstFrameToFillPtr
FirstFrameToFillPtr:
DW 0
MVI M,FrameValid ;Set FrameValid
INX H
SHLD CurrentFrameToFillPtr
XRA A
MOV M,A ;Turn off CompletionCode
STA SdlcFrameFlag
LDA FlowControlFlag
ORA A
CNZ SetLastBufferFlag
POP D
POP H
POP PSW
EI ;Enable interrupts
RET
FifoClosed:
{BC = NextFrameToFillPtr. Test to see that we do not overflow the Fifo. We calculate: CurrentFrameToSendPtr - (NextFrameToFillPtr + MaxFrameSize) If this is negative, we have an overflow condition. MaxFrameSize is the size specified in the client's IOCB + FrameData.}
DB opLXID ;DE ← CurrentFrameToSendPtr
CurrentFrameToSendPtr:
DW 0
DB opLXIH ;DE ← MaxFrameSize
MaxFrameSize:
DW 0
DAD B ;HL ← MaxFrameSize+NextFrameToFillPtr
MOV A,E
SUB L ;Subtract from CurrentFrameToSendPtr
MOV A,D
SBB H
JM FifoOverflow
{Continue if we have room to start a new frame. Now we can initialize CurrentFrameToFillPtr.}
MOV D,B
MOV E,C ;DE ← CurrentFrameToFillPtr
LXI H,FrameData
DAD D ;HL ← new NextRxCharPtr
XCHG ;HL ← CurrentFrameToFillPtr
MOV B,D
MOV C,E ;BC ← NextRxCharPtr
MVI M,FrameValid ;Set FrameValid
INX H
SHLD CurrentFrameToFillPtr
XRA A
MOV M,A ;Turn off CompletionCode
STA SdlcFrameFlag
LDA FlowControlFlag
ORA A
CNZ SetLastBufferFlag
POP D
POP H
POP PSW
EI ;Enable interrupts
RET
;
FifoOverflow:
{We don't have room to start a new frame. We will store the next frame right over the current frame. Set CompletionCode back to empty so the GetLoop won't think this frame is ready. When the next frame completes, we will OR the CompletionCode with OverflowFlag to signal the loss of this frame to the GetLoop.}
MVI A,FifoOverflowed
STA OverflowFlag ;Note overflow for next time through EndOfFrame
LHLD CurrentFrameToFillPtr
{This is pre-Klamath 11.0g code
XRA A
MOV M,A ;Turn off CompletionCode
}
{Instead of simply clearing CompletionCode, which gets us into an infinite loop during aborts we will simply clear EndOfFrame and FifoOverflow. 4-Jan-84}
MOV A,M ;Load Old CompletionCode
ANI 1 ;Mask off all but abort
MOV M,A ;Restore CompletionCode
XRA A ;Clear the SdlcFrameFlag
STA SdlcFrameFlag
LXI D,FrameData-1
DAD D
MOV B,H
MOV C,L
POP D
POP H
POP PSW
EI ;Enable interrupts
RET
;
HandleTxDataInEOF:
{See if the last character has been sent. If the last byte has already been sent, we don't have to worry about clearing the interrupt.}
DB opMVIA ;A ← PutCompletedFlag
PutCompletedFlag:
DB 0FFH ;Initialized to TRUE since no PUTS in progress
ORA A
JNZ StoreFrameHeader
LHLD TxBufferPointer
MOV A,M ;Get character from buffer
OUT TxData ;send byte
DB opMVIA ;A ← LastTxBufferAddressLow
LastTxBufferAddressLow1:
DB 0
CMP L
JNZ UpdateTxBufferPointer
DB opMVIA ;A ← LastTxBufferAddressHigh
LastTxBufferAddressHigh1:
DB 0
CMP H
JNZ UpdateTxBufferPointer
{We have just sent the last byte. The next time we get a TxData interrupt, we will JMP to NoMoreBytesToSend.}
MVI A,opJMP
STA PutFinishedSwitch
STA PutCompletedFlag
JMP StoreFrameHeader
UpdateTxBufferPointer:
INX H ; increment buffer pointer
SHLD TxBufferPointer ; update buffer pointer
JMP StoreFrameHeader
;
SetLastBufferFlag:
{This subroutine which sets or clears the Last buffer flag assumes that the CurrentFrameToFillPtr (CFTF↑) is in the HL registers and the RxFifoState instruction has been set correctly for when the Fifo is open or Closed. (See description of the Get Fifo processing ). The routine implements the following algorithm for setting/clearing the LastBufferFlag.
IF (CurrentFrameToSendPtr<=(CurrentFrameToFillPtr+(2*MaxFrameSize)) AND fifoState = Closed)
OR (CurrentFrameToSendPtr<FirstFrameBoundary+138 AND
RxFifoEnd<=(CurrentFrameToFillPtr+(2*MaxFrameSize)) AND fifoState = Open)
THEN LastBufferFlag ← TRUE
ELSE LastBufferFlag ← FALSE;
}
XCHG
LHLD MaxFrameSize
DAD H
DAD D ;HL ← CFTF + 2*MaxFrameSize
XRA A
RxFifoState:
DB opJNZ ;determine if Open or Closed
DW CloseState
OpenState:
DB opLXID ;D ← RxFifoEnd
RxFifoEnd:
DW 0
MOV A,L ;If HL-RxfifoEnd<0 THEN clearFlag
SUB E
MOV A,H
SBB D
JM ClearFlag
LHLD FirstMaxFrameBoundary
LXI D,138 ;If (FirstMaxFrameBoundary+138
DAD D ; - CurrentFrameToSendPt) < 0
;THEN clear LastBufferFlag
CloseState:
XCHG
LHLD CurrentFrameToSendPtr
MOV A,E ;IF (DE-CurrentFrameToSendPtr)<0
SUB L
MOV A,D
SBB H
JM ClearFlag ;THEN clear LastBufferFlag
CMA ;ELSE set LastBufferFlag
STA LastBufferFlag
RET
ClearFlag:
XRA A
STA LastBufferFlag
RET
;
CheckFifoBoundary:
DB opMVIA ;A ← LastBufferFlag
LastBufferFlag:
DB 0 ;IF LastBufferFlag
ORA A
RZ
LDA FCState ;AND ~(sendingXoff | XoffSent)
ANI SendingXoff+XoffSent
RNZ
LHLD CurrentFrameToSendPtr ;THEN IF ( BC<cfts<=(BC+HWM) )
MOV A,L
SUB C
MOV L,A
MOV A,H
SBB B ;note effect below
MOV H,A
XCHG
LHLD HighWaterMark
JP ClosedFifoState ;From SBB instruction above
OpenFifoState: ;OR IF (BC<RXFIFOEnd<=(BC+HWM) )
DAD B
XCHG
LHLD RxFifoEnd
XCHG
ClosedFifoState:
MOV A,L
SUB E
MOV A,H
SBB D
RM ; THEN send XoffChar
{The following are the routines are called to send an Xon or Xoff char. Because they modify interrupt vector addresses and the FCState variable, interrupts must be disabled before calling these routines.
CAUTION: These routines make reference to absolute address 2060H, which is the address of the AsyncJumpTable defined in Common.asm. }
OutputXoff:
LDA PutCompletedFlag ;IF ~PutCompleted
ORA A
JNZ NoTxOut
LDA FCState
ANI StoppedTx ;AND ~TxStopped
LXI H,Xoff1 ;THEN set Interrupt handler to Xoff1
JZ SetXoffFlags
LDA FCState
ORI XoffSent
STA FCState
LDA XoffChar
OUT TxData
RET
NoTxOut:
LDA XoffChar
OUT TxData ;ELSE send Xoff Character
LXI H,Xoff2 ; set Interrupt handler to Xoff2
SetXoffFlags:
SHLD 2060H+1 ;AsyncJumpTable+1
LDA FCState
ORI SendingXoff
STA FCState
RET
;
OutputXon:
LXI H,Xoff1
LDA PutCompletedFlag ;IF PutCompleted
ORA A
LDA FCState
MOV E,A
JZ PutinProgress ; -- (from ORA instruction above)
LXI H,Xoff2
ANI SendingXoff ; AND ~SendingXoff
JNZ XoffinProgress
LDA XonChar
OUT TxData ;THEN send Xon Character
MOV A,E
ANI -XoffSent-1 ; clear XoffSent Flag
;;;;; ORI SendingXon ; set SendingXon Flag
STA FCState
SHLD 2060H+1 ;AsyncJumpTable+1
RET
PutinProgress:
MOV A,E
ANI StoppedTx ;IF Xoff received and TX has stopped
JNZ DoIt ;THEN output Xon anyway
XoffinProgress:
MOV A,E ;ELSE set SendXon flag
ORI SendXon
STA FCState ;set Interrupt handler
SHLD 2060H+1 ;AsyncJumpTable+1
RET
DoIt:
LDA XonChar
OUT TxData ;THEN send Xon Character
MOV A,E
ANI -XoffSent-1 ; clear XoffSent Flag
STA FCState
RET
;
{The following routines are interrupt handlesrs for the flow control characters which have been or need to be sent. The routine Xoff1 will be invoked if a transmission is already in progress when an Xon/Xoff character needs to be sent. Xoff2 is used when no transmission is currently going on so that after transmitting the character, the TxAsyncData interrupt will be reset.}
Xoff1:
LDA FCState
MOV L,A
ANI SendingXoff+XoffSent ;IF SendingXoff AND ~XoffSent
CPI SendingXoff
JNZ TestforSendXon
LDA XoffChar ;THEN send Xoff and return
OUT TxData
MOV A,L
ORI XoffSent
STA FCState
JMP ReturnXoff1
TestforSendXon:
MOV A,L ;ELSE IF SendXon
ANI SendXon
JZ ResetFlags
LDA XonChar
OUT TxData ;THEN send Xon
MOV A,L
ANI -(SendXon+SendingXoff+XoffSent)-1
STA FCState
LXI H,TxAsyncDataInt ; set interrupt vector to normal
SHLD 2060H+1 ;AsyncJumpTable+1
ReturnXoff1:
POP H
POP PSW
EI
RET
ResetFlags:
MOV A,L ;ELSE reset flags
ANI -SendingXoff-1
;;;;; ORI XoffSent
STA FCState ; set interrupt vector to normal
LXI H,TxAsyncDataInt
SHLD 2060H+1 ;AsyncJumpTable+1
PCHL ; goto transmit next character
;
Xoff2:
DB opMVIA ;A ← FCState
FCState:
DB 0
MOV L,A
ANI SendXon ;IF SendXon
JZ CheckSendXoff
LDA XonChar
OUT TxData ;THEN send Xon
MOV A,L
ANI -(SendXon+SendingXoff+XoffSent)-1
;;;;; ORI SendingXon
STA FCState
JMP ReturnXoff2
CheckSendXoff:
MOV A,L ;ELSE IF SendingXoff
ANI SendingXoff
JZ Clear
MOV A,L
ANI -SendingXoff-1 ;THEN clear SendingXoff
ORI XoffSent
;;;;; JMP TestforPut
TestforPut:
STA FCState
Clear:
LXI H,TxAsyncDataInt;set interrupt vector to normal
SHLD 2060H+1 ;AsyncJumpTable+1
LDA PutCompletedFlag
ORA A ;IF ~PutCompleted THEN
JNZ ResetTx
PCHL ; goto transmit next character
ResetTx:
MVI A,ResetTxIntPending
OUT TxCont ;ELSE clear interrrupt pending
ReturnXoff2:
POP H
POP PSW
EI
RET
END