{ File: [Iris]<WMicro>DLion>CPSubs.asm
Modification History:
Last Change by Dennis Grundler 1-Sep-84 17:05:19 Add copyright notice.
Jim Frandeen April 29, 1982 1:10 PM
Rewritten by Jim Frandeen to move page mapping and page crossing into the CP microcode (March 10, 1982 7:31 AM)
Created by Roy Ogus (May 13, 1980 3:37 PM)}
{ Copyright (C) 1980, 1982, 1983 by Xerox Corporation. All rights reserved.}
; DEFINITION FILES:
get "SysDefs"
get "CommonDefs"
IMP ErrorReport ;From Common
IMP PortBusyFlag ;From BookKeepingTask
IMP ReadDmaCompletion ;From DmaSubs
IMP StartCPDmaChannel ;From DmaSubs
EXP ByteToWord
EXP CheckCPDMAComplete
EXP Copy
EXP DoNakedNotify
EXP LastPCB ;***DEBUGGING
EXP ReadCPbuffer
EXP StartCPReadDMA
EXP StartCPWriteDMA
EXP WordToByte
EXP WriteCPbuffer
{NOTES:
These are the subroutines which handle transfers through the CP-IOP port. Clients using the port should use them to carry out any transfers. The client should first check PortBusyFlag to be sure the CP port is not busy. The flag is set whenever the CP DMA channel is started, and it is reset whenever the CP DMA channel completes.
For most operations, a pointer to a CP port control block (PCB) is passed as a parameter. The PCB contains the information needed to carry out the transfer, viz. The CP Address, the number of bytes to be transferred, and the IOP address. The CP address is always virtual.
CP Port Control Block Format:
byte 0,1, 2: CP buffer pointer (low bytes first),
byte 3: Not used
byte 4,5: CP Buffer Size (in bytes),
byte 6,7: IOP buffer pointer.
NOTE: the last two words of the PortControlBlock are the same as the DmaControlBlock.
The following are the subroutines available to clients:
Transfer subroutines:
- ReadCPbuffer [HL: POINTER to CPPortControlBlock]
- WriteCPbuffer [HL: POINTER to CPPortControlBlock]
- StartCPReadDMA [HL: POINTER to CPPortControlBlock]
- StartCPWriteDMA [HL: POINTER to CPPortControlBlock]
- CheckCPDMAComplete [] RETURNS [ConditionCode: DMAComplete]
Naked notify:
- DoNakedNotify [HL: NakedNotifyMask]
Utility subroutines:
- WordToByte [HL: word] RETURNS [HL: word]
- ByteToWord [HL: word] RETURNS [HL: word]
- Copy [HL: POINTER to Source, D,E: POINTER to Destination, C: count]
}
{The following PCB is used to send a NakedNotify. We send the CPAddress just as if it were a normal transfer command.}
NotifyPCB:
DW 0
DW 0
NotifyMask:
DS 2
LastPCB:
DW 0 ;***DEBUGGING
;
ReadCPbuffer:
{Read from CP main memory.
On entry: HL = Pointer to CPport Control Block}
MVI A,CPReadCmd
CALL InitCPCmd ;Initialize the CP port for reads (send address, count)
{On return, count is in D,E and HL points to count high of the PCB}
INX H ;HL points to low IOP address
MOV A,M ;Get low IOP address
INX H ;HL points to high IOP address
MOV H,M
MOV L,A ;HL contains IOP address
{Now do the actual data transfer. The bytes coming from the port are stored directly in memory. The bytes come from the port high byte, low byte.}
ReadCPLoop:
IN CPStatus ;Read the port interrupt bits
ANI CPInIntMask ;CPIn requesting an interrupt?
CZ ReadCPByteNotThere ;z => no interrupt
IN CPIn ;get data
STA HighByte
IN CPStatus ;Read the port interrupt bits
ANI CPInIntMask ;CPIn requesting an interrupt?
CZ ReadCPByteNotThere ;z => no interrupt
IN CPIn ;get data
MOV M,A ;Store low byte
INX H ;Point to high byte
DB opMVIM ;Store HighByte
HighByte:
DB 0
INX H ;Point to next byte
DCX D ;Check for end of buffer
DCX D ;decrement 2 for 2 bytes
MOV A,D
ORA E ;Test high and low for zero
JNZ ReadCPLoop
RET
WriteCPbuffer:
{Write into CP main memory.
On entry: HL = Pointer to CPport Control Block}
MVI A,CPWriteCmd
CALL InitCPCmd ;Initialize the CP port for writes (send address, count)
; On return, count is in D,E and HL points to count high of the PCB
INX H ;HL points to low IOP address
MOV A,M ;Get low IOP address
INX H ;HL points to high IOP address
MOV H,M
MOV L,A ;HL contains the IOP address
{The bytes coming from the memory are stored directly into the port. The bytes must be sent high byte, low byte.}
WriteCPLoop:
MOV A,M ;Get low byte
PUSH PSW ;Save low byte in Stack
INX H ;Point to high byte
MOV A,M ;Get high byte
OUT CPOut ;Output data
IN CPStatus ;Read the port interrupt bits
ANI CPOutIntMask ;data read?
CZ WriteCPByteNotThere ;Zero means no interrupt
POP PSW ;Get low byte in A
OUT CPOut ;Output data
IN CPStatus ;Read the port interrupt bits
ANI CPOutIntMask ;data read?
CZ WriteCPByteNotThere ;Zero means no interrupt
INX H ;Point to next byte
DCX D ;Check for end of buffer
DCX D ;decrement 2 for 2 bytes
MOV A,D ;Check high and low for zero
ORA E
JNZ WriteCPLoop
RET
StartCPReadDMA:
{Start the DMA controller reading a buffer from CP main memory to IOP memory. The address in CP main memory is a virtual address. The Client will wait for the CP Dma completion.
On entry: HL = Pointer to CPport Control Block}
MVI A,CPReadCmd ;Write command
CALL InitCPCmd ;Initialize the CP port for reads
{On return, count is in D,E and HL points to count high of PCB}
DCX H ;Point to Dma Control Block
MVI A,DmaFuncWrite ;Dma write
CALL StartCPDmaChannel
{Dma is now programmed and enabled. Start Dma going. At this point IOPWait, and SwTAddr are cleared.}
MVI a,CPEnable+CPDmaIn
OUT CPControl ;Set DmaIn (Clear IOPWait, clear SwTAddr, DmaMode)
MVI a,CPEnable+CPDmaIn+CPDmaMode
OUT CPControl ;Set DmaMode (set DmaIn, clear IOPWait, SwTAddr)
STA PortBusyFlag
RET
StartCPWriteDMA:
{Start the DMA controller writing a buffer to CP main memory from IOP memory. The address in CP main memory is a virtual address. The Client will wait for the CP Dma completion.
On entry: HL = Pointer to CPport Control Block}
MVI A,CPWriteCmd ;Write command
CALL InitCPCmd ;Initialize the CP port for writes
{On return, count is in D,E and HL points to count high of PCB}
DCX H ;Point to Dma Control Block
MVI A,DmaFuncRead ;Dma read
CALL StartCPDmaChannel
{Dma is now programmed and enabled. Start Dma going. At this point IOPWait, and SwTAddr are cleared.}
MVI a,CPEnable+CPDmaMode
OUT CPControl ;Set DmaMode (clear IOPWait, SwTAddr, DmaIn)
STA PortBusyFlag
RET
CheckCPDmaComplete:
{Check for the CP DMA channel completion. If the channel has completed, clear the DMA channel, and CP Dma hardware.
On exit, condition code is set:
zero => channel is not complete
# Zero => channel has completed, DMA channel has been cleared.}
IN CPStatus ;Check for completion in CPStatus register
ANI CPDmaCompMask
RZ ;z => channel has not completed
MVI E,CPChannelMask ;Check internal completion.
CALL ReadDmaCompletion ; Get completion
JZ NoCPDmaComplete ;nz => Completed
CPDmaCompleted:
{The CP channel completed correctly. The Dma controller hardware is disabled, and ReadDmaCompletion will clear the channel in DmaActive. Thus, don't CALL ClearDmaChannel. Clear the external Dma completion bit, and clear DmaMode. We also clear PortBusyFlag here.}
XRA A
STA PortBusyFlag
OUT CPDmaClr ;Clear CPDmaComplete bit
MVI A,CPEnable
OUT CPControl ;Clear DmaMode (clear Wait, SwTAddr, DmaIn)
ORA A ;Set condition code for completed
RET
NoCPDmaComplete:
{The Dma channel did not internally complete. This indicates a fatal error.}
LXI h,ErrorNoCPDmaComplete ;ERROR: Internal CP Dma channel did not complete
JMP ErrorReport
DoNakedNotify:
{On entry: HL = Notify mask.}
SHLD NotifyMask ;Save mask in NotifyPCB
LXI H,NotifyPCB ;Point to NotifyPCB
MVI A,CPNotifyCmd
{Fall through to InitCPCmd}
;
InitCPCmd:
{Initialize CP port for reads or writes.
On entry:
A = command to microcode
HL = Pointer to CPport Control Block
Format of initialize:
low address, middle address, high address, low count, high count, Command.
On Exit:
HL = pointer to count high of CP port Control Block
DE contains count in bytes or NakedNotify mask}
PUSH PSW ;Save command in A
SHLD LastPCB ;***DEBUGGING
MOV A,M ;Address byte 3
OUT CPOut ;Output data
IN CPStatus ;Read the port interrupt bits
ANI CPOutIntMask ;data read?
CZ WriteCPByteNotThere ;Zero means no interrupt
INX H
MOV A,M ;Address byte 2
OUT CPOut ;Output data
IN CPStatus ;Read the port interrupt bits
ANI CPOutIntMask ;data read?
CZ WriteCPByteNotThere ;Zero means no interrupt
INX H
MOV a,M ;High part address (address byte 1)
OUT CPOut ;Output data
IN CPStatus ;Read the port interrupt bits
ANI CPOutIntMask ;CPOut requesting an interrupt, i.e data read?
CZ WriteCPByteNotThere ;Zero means no interrupt
INX H ;Point to byte not used
INX H ;Point to count low
MOV A,M ;Low part count
MOV E,A ;DE will contain count on return
OUT CPOut ;Output data
IN CPStatus ;Read the port interrupt bits
ANI CPOutIntMask ;CPOut requesting an interrupt, i.e data read?
CZ WriteCPByteNotThere ;Zero means no interrupt
INX H ;Point to count high
MOV A,M ;D,E contains count
MOV D,A
OUT CPOut ;Output data
IN CPStatus ;Read the port interrupt bits
ANI CPOutIntMask ;CPOut requesting an interrupt, i.e data read?
CZ WriteCPByteNotThere ;Zero means no interrupt
POP PSW ;A ← command
OUT CPOut ;Output data
IN CPStatus ;Read the port interrupt bits
ANI CPOutIntMask ;CPOut requesting an interrupt, i.e data read?
CZ WriteCPByteNotThere ;Zero means no interrupt
RET
WriteCPByteNotThere:
{WriteCP byte is not there. Check for timeout.}
CMA ;A ← FF
UpdateWriteTimeout:
STA WriteTimeout
IN CPStatus ;Read the port interrupt bits
ANI CPOutIntMask ;CPOut requesting an interrupt, i.e data read?
RNZ
DB opMVIA ;A ← WriteTimeout
WriteTimeout:
DB 0FFH
DCR A
JNZ UpdateWriteTimeout
WritePortTimeOut:
LXI h,ErrorWriteCPPortDead ;Set up error code for read timeout
JMP PortTimeOut
ReadCPByteNotThere:
{ReadCP byte is not there. Check for timeout.}
CMA ;A ← FF
UpdateReadTimeout:
STA ReadTimeout
IN CPStatus ;Read the port interrupt bits
ANI CPInIntMask ;CPIn requesting an interrupt?
RNZ
DB opMVIA ;A ← ReadTimeout
ReadTimeout:
DB 0
DCR A
JNZ UpdateReadTimeout
ReadPortTimeOut:
LXI H,ErrorReadCPPortDead ;Set up error code for read timeout
PortTimeOut:
{We have a timeout. Check for CP parity error first.}
IN MiscInput1
ANI CSParityMask
JNZ ErrorReport ;z => (active low) it was
LXI h,ErrorCSParity ;ERROR: CS parity error
JMP ErrorReport
;
ByteToWord:
{Convert value in HL (byte count) to word count. i.e. long shift right by 1.}
XRA A ;Clear carry
MOV A,H ;High part
RAR ;shift high part right, low bit into carry
MOV H,A
MOV A,L ;Low part
RAR ;shift low part right, shift in high bit
MOV L,A
RET
WordToByte:
{Convert value in HL (word count) to byte count. i.e. long shift left by 1.}
XRA A ;Clear carry
MOV A,L ;Low part
RAL ;shift low part, high bit into carry
MOV L,A
MOV A,H ;High part
RAL ;shift high part, shift in low bit
MOV H,A
RET
Copy:
{On entry:
HL = Source address
DE = Destination address
A = Count (in bytes). Count=0 => 256.
On exit:
HL = Next Source address after copy
DE = Next Destination address after copy}
STA CopyCount
MOV A,M ;Get source byte
STAX D ;Store away in destination
INX H ;Increment pointers
INX D
DB opMVIA ;A ← CopyCount
CopyCount:
DB 0
DCR A ;Check count
JNZ Copy
RET
END CPSubs