{File: RS232CPut.asm
Modification History:
Last change by Jim Frandeen : September 8, 1982 3:08 PM: Zero Success bit in case or error. Change handling of Abort.
Last change by Jim Frandeen : July 30, 1982 1:33 PM: new IO Page Format
Created by Jim Frandeen : March 26, 1982 8:23 AM
}

Get"SysDefs"
Get"CommonDefs"
Get"RS232CDefs"

IMPCheckCPDmaComplete;From CPSubs
IMPCurrentSyncCount;From RS232CMisc
IMPDisableTxCRC1;From BisyncInterrupts
IMPDoNakedNotify;From MultiTask
IMPLastTxBufferAddressHigh;From RS232CInterrupts
IMPLastTxBufferAddressHigh1;From RS232CInterrupts
IMPLastTxBufferAddressLow;From RS232CInterrupts
IMPLastTxBufferAddressLow1;From RS232CInterrupts
IMPPortBusyFlag;From CPSubs
IMPPutCompletedFlag;From RS232CInterrupts
IMPPutFinishedSwitch;From RS232CInterrupts
IMPReadCPBuffer;From CPSubs
IMPRS232CMode;From RS232CMisc
IMPRS232CPutFlag;From BookKeepingTask
IMPRS232CTaskWakeMask;From RS232CMisc
IMPStartCPReadDma;From CPSubs
IMPSyncCharacterCount;From BisyncInterrupts
IMPTxBisyncInitial;From BisyncInterrupts
IMPTxBuffer;From Buffer
IMPTxBufferPointer;From RS232CInterrupts
IMPTxBisyncStateSwitch;From BisyncInterrupts
IMPTxWR1;From RS232CMisc
IMPTxWR5;From RS232CMisc
IMPWriteCPBuffer;From CPSubs
IMPZeroCommand;From CPSubs

EXPAbortPutFlag
EXPIllegalBisyncCharacterFlag
EXPPutAsyncMode
EXPPutBisyncMode
EXPPutSdlcMode
EXPPutModeSwitch
EXPPutTaskResumeAddress
EXPPutTaskQuiet
EXPRestartSIOCharacter
EXPStartPutTask
EXPTxUnderrunDetectedFlag
;
RS232CPutTask:
DBopJMP;JMP PutTaskYield
PutTaskResumeAddress:
DWPutTaskQuiet;Set by Misc Off Command
;
JMPStartPutTask;Set by Misc SetParameters Command
;
JMPFetchPutIocb
;
JMPWaitForPutBlock
;
JMPWaitForAsyncPut
;
JMPWaitForSdlcPut
;
JMPWaitForBisyncPut
;
JMPTerminateTransmit

RS232CPutCSB:

PutIOCBAddressPointerLo:
DS2;14031
PutIOCBAddressPointerHi:
DS2;14032

{Put IOCB: this IOCB is copied from the CP into this buffer. When the IOCB has been processed, it is sent back to the CP.}
PutIocbBuffer:
PutBufferAddressLo:
DW0; Virtual Address of Put Buffer
PutBufferAddressHi:
DW0
PutByteCount:
DW0; Size of Data in bytes
PutDataByteCount:
DW0; Number of bytes actually sent.
PutTransferStatus:

DB0
PutTransferStatusHi:
DB0
PutIocbSize
EQU10

{Port Control Block to read and write the above IOCB}
PutIocbPCB:
PutPCBAddressLo:
DW0; Address of IOCB in CP memory
PutPCBAddressHi:
DW0
DWPutIocbSize;Size of IOCB in bytes
DWPutIocbBuffer; points to IOCB above

{Port Control Block to send CSB back to CP}
ReadPutPCB:
DWRS232CPutCSBLoc; Address in IO Page
DWCPIOPageHi
DWRS232CPutCSBSize; Size of CSB (in bytes)
DWRS232CPutCSB; Pointer to IOP Buffer

{Port Control Block to Zero Put Flag}
ZeroPutFlag:
DWRS232CPutFlagLoc; Address in IO Page
DWCPIOPageHi
DW2; Size of CSB (in bytes)
DWZeroCommand; Pointer to IOP Buffer

{PortControlBlock to read block of data from the CP.}
TxBlockPCB:
TxBlockAddressLo:
DW0;Address Of Buffer in Main Memory
TxBlockAddressHi:
DW0
TxBlockByteCount:
DW0;Transfer data Count (in bytes)
DWTxBuffer;Read into TxBuffer
;
StartPutTask:
{Come here when the Misc On Command sets the resume address to start this task.}

FetchPutIocb:
LDARS232CPutFlag; Check if bit 0 #0 (full)
ORAA; Set Flags
JNZReadPutCSB

{Nothing to do since the Put Flag is not on. Turn off AbortPutFlag in case Misc is waiting for it.}
PutTaskQuiet:
XRAA
STAAbortPutFlag
JMPPutTaskYield

ReadPutCSB:
LDAPortBusyFlag
ORAA
JNZPutTaskYield

LXIH,ReadPutPCB
CALLReadCPBuffer;Read CSB

{Move the IOCB address from the IOPage into the ReadBlock command.}
LHLDPutIocbAddressPointerLo
SHLDPutPCBAddressLo
LHLDPutIocbAddressPointerHi
SHLDPutPCBAddressHi

{Copy IOCB from main memory into IOP memory defined above.}
{NOTE: The IOCB should be in the IOPage. Then we won’t have to copy it.}
LXIH,PutIocbPCB
CALLReadCPBuffer

{Prepare to fetch the block of data from the CP. Copy the address of the data and the byte count from the IOCB into the Transfer ControlBlock.}
{NOTE: the IOCB should be in the format of a DmaControlBlock.}
LHLDPutBufferAddressLo
SHLDTxBlockAddressLo
LHLDPutBufferAddressHi
SHLDTxBlockAddressHi
LHLDPutByteCount; H,L contains number of bytes in block
SHLDPutDataByteCount; Assume we will put that many bytes

{Check to see if the data will fit in the TxBuffer.}
{NOTE: The head should do this.}
LXID,TxBufferSize
MOVA,E
SUBL
MOVA,D
SBBH
JMPutBlockTooBig; Jump if block is too big

{Calculate the address of the last character so the interrupt routine will know when it is finished.}
LXID,TxBuffer
DADD
DCXH;HL = last address
MOVA,L
STALastTxBufferAddressLow
STALastTxBufferAddressLow1
MOVA,H
STALastTxBufferAddressHigh
STALastTxBufferAddressHigh1

{Initialize the instruction at PutFinishedSwitch. This gets modified by the interrupt routine when it sends the last byte.}
MVIA,opJNZ
STAPutFinishedSwitch

{Make sure the number of bytes is even.}
LHLDPutByteCount
INXH
MOVA,L
ANI0FEH
MOVL,A
SHLDTxBlockByteCount

{Start the command to read the block from the CP.}
LXIH,TxBlockPCB
CALLStartCPReadDma
LXIH,WaitForPutBlock
JMPSavePutTaskResumeAddressAndYield

WaitForPutBlock:
CALLCheckCPDmaComplete
JZPutTaskYield

{Now we have the data in memory. Perform initialization common to Async, Bisync, and Sdlc.}
XRAA
STATxUnderrunDetectedFlag
STAPutCompletedFlag
LXIH,XferSuccess;Assume successful transfer
SHLDPutTransferStatus
MVIA,ResetTxCRCGenerator
OUTTxCont

{Jump to the appropriate routine. The following JMP address is modified by Misc when it processes a SetParameters command.}

DBopJMP;JMP PutAsyncMode
PutModeSwitch:
DWPutAsyncMode
;
JMPPutAsyncMode
;
JMPPutBisyncMode
;
JMPPutSdlcMode}
;
PutBisyncMode:
XRAA
STAIllegalBisyncCharacterFlag
STARestartSIOCharacter
DI
STAPutCompletedFlag
LXIH,TxBisyncInitial
SHLDTxBisyncStateSwitch
EI
LXIH,TxBuffer;Initialize TxBufferPointer
SHLDTxBufferPointer
LDACurrentSyncCount
STASyncCharacterCount
LXIH,8000H+TxCont
LDADisableTxCRC1
DI
MVIM,PointToWR5
MOVM,A;Disable CRC generator
EI
MVIA,ModemSYN
OUTTxData; send the first byte to the chip
LXIH,WaitForBisyncPut
JMPSavePutTaskResumeAddressAndYield

WaitForBisyncPut:
{We won’t get the first interrupt for TxBufferEmpty until we have sent one character. Wait for the interrupt routine to send all the rest of the characters. }

DBopMVIA;A ← RestartSIOCharacter
RestartSIOCharacter:
DB0
ORAA
JNZPrimeSIO

LDAPutCompletedFlag
ORAA
JZPutTaskYield

DBopMVIA;A ← IllegalBisyncCharacterFlag
IllegalBisyncCharacterFlag:
DB0
ORAA
JZTerminateTransmit

MVIL,InvalidCharacter
JMPStoreErrorStatus

PrimeSIO:
{The interrupt routine wants us to prime the chip by sending a character. See if this is the filler character for end of frame.}
CPIFiller
JNZRestartSIO

{If this is a filler character, wait until we get underrun. This means the CRC has been sent. We must not restart the chip until the CRC has been sent.}
LDATxUnderrunDetectedFlag
ORAA
JZPutTaskYield

MVIA,Filler

RestartSIO:
OUTTxData; send the first byte to the chip
XRAA
STARestartSIOCharacter
JMPPutTaskYield
;
PutSdlcMode:
LXIH,TxBuffer
MOVA,M; get the first character
INXH; point to next byte
SHLDTxBufferPointer
OUTTxData; send the first byte to the chip

{When in Sdlc mode, we must reset the TxUnderRun/EOM flag after sending the first byte of message. This causes the CRC to be sent at the end of the frame.}

MVIA,ResetTxEOM
OUTTxCont
LXIH,WaitForSdlcPut
JMPSavePutTaskResumeAddressAndYield

{Wait for the interrupt routine to send all the rest of the characters.}
WaitForSdlcPut:
DBopMVIA;A ← AbortPutFlag
AbortPutFlag:
DB0
ORAA
JNZAbortPut

DBopORI;A ← TxUnderrunDetectedFlag
TxUnderrunDetectedFlag:
DB0
JZPutTaskYield

{Continue if underrun has been detected. See if all the data has been transferred. If the Put did not complete, and we got an illegal transmit underrun, abort this frame.}
LDAPutCompletedFlag
ORAA
JNZTestBufferEmpty

AbortPut:
MVIA,SendAbort
OUTTxCont
MVIL,Aborted
JMPStoreErrorStatus

PutBlockTooBig:
MVIL,DeviceError

StoreErrorStatus:
MVIH,0;Zero success bit
SHLDPutTransferStatus

{Compute number of bytes actually sent: TxBufferPointer - TxBuffer.}
LHLDTxBufferPointer
XCHG;DE ← TxBufferPointer
LXIH,TxBuffer
MOVA,D
SUBL;Subtract TxBuffer
MOVL,A
MOVA,E
SBBH
MOVH,A
SHLDPutDataByteCount
JMPTerminateTransmit
;
PutAsyncMode:
LXIH,TxBuffer
MOVA,M; get the first character
INXH; point to next byte
SHLDTxBufferPointer
OUTTxData; send the first byte to the chip
LXIH,WaitForAsyncPut
JMPSavePutTaskResumeAddressAndYield

WaitForAsyncPut:
{We won’t get the first interrupt for TxBufferEmpty until we have sent one character. Wait for the interrupt routine to send all the rest of the characters. It must be the case that we have sent all of the characters AND we have received the last interrupt from the chip. If we haven’t received the last interrupt from the chip, there will still be a character in the transmit buffer, and it will not get sent until the next frame.}

LDAPutCompletedFlag
ORAA
JZPutTaskYield; if not done

TestBufferEmpty:
INTxCont; Check to see if the buffer is empty
ANITxBufferEmpty
JZPutTaskYield

TerminateTransmit:
{Send the processed IOCB back to the CP.}
LDAPortBusyFlag
ORAA
JZReturnPutIocb
LXIH,TerminateTransmit
JMPSavePutTaskResumeAddressAndYield

ReturnPutIocb:
{Clear Put Abort flag in case it was set. This is used to signal the AbortOutput command in RS232CMisc that the task has completed.}
STAAbortPutFlag
LXIH,PutIocbPCB
CALLWriteCPBuffer

{Zero the PutFlag to indicate that the IOCB has been processed, and we are ready for another.}
LXIH,ZeroPutFlag
CALLWriteCPBuffer
LHLDRS232CTaskWakeMask
CALLDoNakedNotify
LXIH,FetchPutIocb

SavePutTaskResumeAddressAndYield:
SHLDPutTaskResumeAddress
PutTaskYield:
{Pass control to the next task specified in Domino.cfg}
DS0

END