{ File: [Idun]<WDLion>CPSubs.asm Modification History: Last Change by 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)} ; 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