{File: [Iris]<WMicro>DLion>RS232CPut.asm
Modification History:
	Dennis Grundler:	 1-Sep-84 17:14:26:  Added copyright notice
	Mike Thatcher:	 27-Mar-84  9:41:24:  Corrected minor glitch with flow control
	Mike Thatcher:	 22-Dec-83 15:55:11:  added Async Flow Control
	Dennis Grundler:  9-Oct-83 16:28:53: fix problem with sending character before TxBufferEmpty.
	Dennis Grundler:  3-Oct-83 10:13:28: fix abort for bisync and async modes.
	Jim Frandeen :  September 8, 1982  3:08 PM: Zero Success bit in case or error. Change handling of Abort.
	Jim Frandeen :  July 30, 1982  1:33 PM: new IO Page Format
Created  by Jim Frandeen : March 26, 1982  8:23 AM
}

{ 	Copyright (C) 1982, 1983 by Xerox Corporation.  All rights reserved.}


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

	IMP	CheckCPDmaComplete	;From CPSubs
	IMP	CurrentSyncCount	;From RS232CMisc
	IMP	DisableTxCRC1		;From BisyncInterrupts
	IMP	DoNakedNotify		;From MultiTask
	IMP	FlowControlFlag
	IMP	FCState
	IMP	LastTxBufferAddressHigh	;From RS232CInterrupts
	IMP	LastTxBufferAddressHigh1	;From RS232CInterrupts
	IMP	LastTxBufferAddressLow	;From RS232CInterrupts
	IMP	LastTxBufferAddressLow1	;From RS232CInterrupts
	IMP	PortBusyFlag		;From CPSubs
	IMP	PutCompletedFlag	;From RS232CInterrupts
	IMP	PutFinishedSwitch	;From RS232CInterrupts
	IMP	ReadCPBuffer		;From CPSubs
	IMP	RS232CMode		;From RS232CMisc
	IMP	RS232CPutFlag		;From BookKeepingTask
	IMP	RS232CTaskWakeMask	;From RS232CMisc
	IMP	StartCPReadDma		;From CPSubs
	IMP	SyncCharacterCount	;From BisyncInterrupts
	IMP	TxBisyncInitial		;From BisyncInterrupts
	IMP	TxBuffer		;From Buffer
	IMP	TxBufferPointer		;From RS232CInterrupts
	IMP	TxBisyncStateSwitch	;From BisyncInterrupts
	IMP	TxWR1			;From RS232CMisc
	IMP	TxWR5			;From RS232CMisc
	IMP	WriteCPBuffer		;From CPSubs
	IMP	XoffReceivedFlag
	IMP	ZeroCommand		;From CPSubs

	EXP	AbortPutFlag
	EXP	IllegalBisyncCharacterFlag
	EXP	PutAsyncMode
	EXP	PutBisyncMode
	EXP	PutSdlcMode
	EXP	PutModeSwitch
	EXP	PutTaskResumeAddress
	EXP	PutTaskQuiet
	EXP	RestartSIOCharacter
	EXP	StartPutTask
	EXP	TxUnderrunDetectedFlag
;
RS232CPutTask:
	DB	opJMP	;JMP PutTaskYield
PutTaskResumeAddress:
	DW	PutTaskQuiet	;Set by Misc Off Command
;	JMP	StartPutTask	;Set by Misc SetParameters Command
;	JMP	FetchPutIocb
;	JMP	WaitForPutBlock
;	JMP	WaitForAsyncPut
;	JMP	WaitForSdlcPut
;	JMP	WaitForBisyncPut
;	JMP	TerminateTransmit

RS232CPutCSB:	
PutIOCBAddressPointerLo:
	DS	2		;14031
PutIOCBAddressPointerHi:
	DS	2		;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:
	DW	0		; Virtual Address of Put Buffer
PutBufferAddressHi:
	DW	0
PutByteCount:
	DW	0		; Size of Data in bytes
PutDataByteCount:
	DW	0		; Number of bytes actually sent.
PutTransferStatus:	
	DB	0
PutTransferStatusHi:
	DB	0
PutIocbSize	EQU	10

{Port Control Block to read and write the above IOCB}
PutIocbPCB:
PutPCBAddressLo:
	DW	0		; Address of IOCB in CP memory
PutPCBAddressHi:
	DW	0
	DW	PutIocbSize	;Size of IOCB in bytes
	DW	PutIocbBuffer	; points to IOCB above

{Port Control Block to send CSB back to CP}
ReadPutPCB:
	DW	RS232CPutCSBLoc	; Address in IO Page
	DW	CPIOPageHi	
	DW	RS232CPutCSBSize	; Size of CSB (in bytes)
	DW	RS232CPutCSB	; Pointer to IOP Buffer

{Port Control Block to Zero Put Flag}
ZeroPutFlag:
	DW	RS232CPutFlagLoc; Address in IO Page
	DW	CPIOPageHi	
	DW	2		; Size of CSB (in bytes)
	DW	ZeroCommand	; Pointer to IOP Buffer

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

FetchPutIocb:
	LDA	RS232CPutFlag	; Check if bit 0 #0 (full)
	ORA	A		; Set Flags
	JNZ	ReadPutCSB

{Nothing to do since the Put Flag is not on. Turn off AbortPutFlag in case Misc is waiting for it.}
PutTaskQuiet:
	XRA	A
	STA	AbortPutFlag
	JMP	PutTaskYield

ReadPutCSB:
	LDA	PortBusyFlag
	ORA	A
	JNZ	PutTaskYield

	LXI	H,ReadPutPCB
	CALL	ReadCPBuffer	;Read CSB

{Move the IOCB address from the IOPage into the ReadBlock command.}
	LHLD	PutIocbAddressPointerLo	
	SHLD	PutPCBAddressLo	
	LHLD	PutIocbAddressPointerHi
	SHLD	PutPCBAddressHi

{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.}
	LXI	H,PutIocbPCB
	CALL	ReadCPBuffer

{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.}
	LHLD	PutBufferAddressLo	
	SHLD	TxBlockAddressLo
	LHLD	PutBufferAddressHi
	SHLD	TxBlockAddressHi
	LHLD	PutByteCount	; H,L contains number of bytes in block
	SHLD	PutDataByteCount	; Assume we will put that many bytes

{Check to see if the data will fit in the TxBuffer.}
{NOTE: The head should do this.}
	LXI	D,TxBufferSize
	MOV	A,E
	SUB	L
	MOV	A,D
	SBB	H
	JM	PutBlockTooBig	; Jump if block is too big		

{Calculate the address of the last character so the interrupt routine will know when it is finished.}
	LXI	D,TxBuffer
	DAD	D
	DCX	H		;HL = last address
	MOV	A,L	
	STA	LastTxBufferAddressLow
	STA	LastTxBufferAddressLow1
	MOV	A,H
	STA	LastTxBufferAddressHigh
	STA	LastTxBufferAddressHigh1

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

{Make sure the number of bytes is even.}
	LHLD	PutByteCount
	INX	H
	MOV	A,L
	ANI	0FEH
	MOV	L,A
	SHLD	TxBlockByteCount

{Start the command to read the block from the CP.}
	LXI	H,TxBlockPCB
	CALL	StartCPReadDma
	LXI	H,WaitForPutBlock
	JMP	SavePutTaskResumeAddressAndYield

WaitForPutBlock:
	CALL	CheckCPDmaComplete
	JZ	PutTaskYield

{Now we have the data in memory. Perform initialization common to Async, Bisync, and Sdlc.}
	XRA	A
	STA	TxUnderrunDetectedFlag
	LXI	H,XferSuccess	;Assume successful transfer
	SHLD	PutTransferStatus
	MVI	A,ResetTxCRCGenerator
	OUT	TxCont

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

	DB	opJMP	;JMP PutAsyncMode
PutModeSwitch:
	DW	PutAsyncMode
;	JMP	PutAsyncMode
;	JMP	PutBisyncMode
;	JMP	PutSdlcMode}
;
PutBisyncMode:
	XRA	A
	STA	IllegalBisyncCharacterFlag
	STA	RestartSIOCharacter
	DI
	STA	PutCompletedFlag
	LXI	H,TxBisyncInitial
	SHLD	TxBisyncStateSwitch
	EI
	LXI	H,TxBuffer	;Initialize TxBufferPointer
	SHLD	TxBufferPointer
	LDA	CurrentSyncCount
	STA	SyncCharacterCount
	LXI	H,8000H+TxCont
	LDA	DisableTxCRC1
	DI
	MVI	M,PointToWR5
	MOV	M,A		;Disable CRC generator
	EI
	MVI	A,ModemSYN
	OUT	TxData	; send the first byte to the chip
	LXI	H,WaitForBisyncPut
	JMP	SavePutTaskResumeAddressAndYield

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. }
	LDA	AbortPutFlag
	ORA	A
	JNZ	AbortPut
	DB	opMVIA		;A ← RestartSIOCharacter
RestartSIOCharacter:
	DB	0
	ORA	A
	JNZ	PrimeSIO

	LDA	PutCompletedFlag
	ORA	A
	JZ	PutTaskYield

	DB	opMVIA		;A ← IllegalBisyncCharacterFlag
IllegalBisyncCharacterFlag:
	DB	0
	ORA	A
	JZ	TerminateTransmit
	
	MVI	L,InvalidCharacter
	JMP	StoreErrorStatus

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.}
	CPI	Filler
	JNZ	RestartSIO

{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.}
	LDA	TxUnderrunDetectedFlag
	ORA	A
	JZ	PutTaskYield
	

	MVI	A,Filler

RestartSIO:
	MOV	D,A		;temporarily save value
	LXI	H,8000H+TxCont	;set up to read SIO register 0
	MVI	M,PointToWR0
	MOV	A,M		;Read SIO register 0.
	ANI	TxBufferEmpty	;check for Tx buffer empty.
	MOV	A,D		;restore value to send without disturbing the flags.
	JZ	PutTaskYield

	OUT	TxData	; send the first byte to the chip
	XRA	A
	STA	RestartSIOCharacter
	JMP	PutTaskYield
;
PutSdlcMode:
	XRA	A
	STA	PutCompletedFlag
	LXI	H,TxBuffer
	MOV	A,M		; get the first character
	INX	H		; point to next byte
	SHLD	TxBufferPointer
	OUT	TxData	; 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.}

	MVI	A,ResetTxEOM 
	OUT	TxCont
	LXI	H,WaitForSdlcPut
	JMP	SavePutTaskResumeAddressAndYield

{Wait for the interrupt routine to send all the rest of the characters.}
WaitForSdlcPut:
	DB	opMVIA	;A ← AbortPutFlag
AbortPutFlag:
	DB	0
	ORA	A
	JNZ	AbortPut

	DB	opORI		;A ← TxUnderrunDetectedFlag
TxUnderrunDetectedFlag:
	DB	0
	JZ	PutTaskYield

{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.}
	LDA	PutCompletedFlag
	ORA	A
	JNZ	TestBufferEmpty

AbortPut:
	MVI	A,SendAbort
	OUT	TxCont
	MVI	L,Aborted
	JMP	StoreErrorStatus

PutBlockTooBig:
	MVI	L,DeviceError

StoreErrorStatus:
	MVI	H,0		;Zero success bit
	SHLD	PutTransferStatus

{Compute number of bytes actually sent: TxBufferPointer - TxBuffer.}
	LHLD	TxBufferPointer
	XCHG			;DE ← TxBufferPointer
	LXI	H,TxBuffer
	MOV	A,D
	SUB	L		;Subtract TxBuffer
	MOV	L,A
	MOV	A,E
	SBB	H
	MOV	H,A
	SHLD	PutDataByteCount
	JMP	TerminateTransmit
;
PutAsyncMode:
	LDA	FlowControlFlag
	ORA	A
	JZ	SendFirstCharacter
	DI
	LDA	XoffReceivedFlag
	ORA	A
	JNZ	Wait
	LDA	FCState
	ANI	SendXon+SendingXoff
	JNZ	Wait
	EI

SendFirstCharacter:
	STA	PutCompletedFlag
	LXI	H,TxBuffer
	MOV	A,M		; get the first character
	INX	H		; point to next byte
	SHLD	TxBufferPointer
	OUT	TxData		; send the first byte to the chip
	LXI	H,WaitForAsyncPut
	JMP	SavePutTaskResumeAddressAndYield

Wait:
	XRA	A
	STA	PutCompletedFlag
	LXI	H,TxBuffer
	SHLD	TxBufferPointer
	EI
	LXI	H,WaitForAsyncPut
	JMP	SavePutTaskResumeAddressAndYield

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.}

	LDA	AbortPutFlag
	ORA	A
	JNZ	AbortPut
	
	LDA	PutCompletedFlag
	ORA	A
	JZ	PutTaskYield	; if not done

TestBufferEmpty:
	IN	TxCont	; Check to see if the buffer is empty
	ANI	TxBufferEmpty
	JZ	PutTaskYield

TerminateTransmit:
{Send the processed IOCB back to the CP.}
	LDA	PortBusyFlag
	ORA	A
	JZ	ReturnPutIocb
	LXI	H,TerminateTransmit
	JMP	SavePutTaskResumeAddressAndYield

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.}
	STA	AbortPutFlag
	LXI	H,PutIocbPCB
	CALL	WriteCPBuffer	

{Zero the PutFlag to indicate that the IOCB has been processed, and we are ready for another.}
	LXI	H,ZeroPutFlag
	CALL	WriteCPBuffer		
	LHLD	RS232CTaskWakeMask
	CALL	DoNakedNotify
	LXI	H,FetchPutIocb

SavePutTaskResumeAddressAndYield:
	SHLD	PutTaskResumeAddress
PutTaskYield:
{Pass control to the next task specified in Domino.cfg}
	DS	0

	END