{ File: [Idun]<WDLion>DmaSubs.asm Modification History: Last change by Jim Frandeen May 21, 1982 9:01 AM: Change DmaControlBlock Format and Port Control Block format to be compatible with each other. Save mode bits in DmaActive. Changed StartDmaChannel so DmaChannel cannot be destroyed by interrupting subroutine (April 3, 1981 7:04 AM). Changed di/ei to use subroutines (March 25, 1981 9:34 PM) Leave Floppy Channel enabled while StartDmaChannel enables another channel in an effort to reduce the LostData errors (January 26, 1981 11:52 AM) protected StartDmaChannel, ClearDmaChannel, ReadDmaCompletion from interference by the Floppy Interrupt (January 25, 1981 4:00 PM) Shortened critical section in StartDmaChannel (January 20, 1981 1:56 PM) Removed DmaCompletion, changed ClearDmaChannel (December 18, 1980 2:09 PM) Shortened critical section in StartDmaChannel (October 31, 1980 12:11 PM) Created by Roy Ogus (June 17, 1980 1:48 PM) } ; DEFINITION FILES: get "SysDefs" ;system defs get "CommonDefs" ;Common defs ; IMPORTS/EXPORTS: IMP ErrorReport EXP ClearDmaChannel EXP DmaActive EXP FloppyActiveSwitch1 EXP FloppyActiveSwitch2 EXP FloppyActiveSwitch3 EXP ReadDmaCompletion EXP StartCPDmaChannel EXP StartDmaChannel EXP StartFloppyChannel { NOTES: These are the subroutines which handle the Dma Controller. Clients using the Dma Controller should use them to set up, start, or disable a particular channel. DmaControlBlock Format: byte 0: Byte count low, byte 1: Byte count high, byte 2: Memory Address Low, byte 3: Memory Address high, NOTE: the DmaControlBlock is the same as the last two words of the PortControlBlock. The following are the subroutines available to clients: - StartDmaChannel: [H,L: POINTER to DmaControlBlock, A: Function/DmaChannelMask] - Function/DmaChannelMask: Function (bits 0,1) : 0 = Verify, 1 = write, 2 = read, 3 = undefined DmaChannelMask (bits 4..7): EN3, EN2, EN1, EN0 Set up the address, count, and operation for the particular Dma channels. Other channels are not affected. Channels which have completed are removed from DmaActive. "DmaActive" is updated in memory. Returns StartError =0 for no error, StartError#0 for error in channel mask. - ReadDmaCompletion [A: DmaChannelMask] RETURNS [A: ~DmaActive AND DmaChannelMask] Read Dma Status, and update DmaActive.Return ~DmaActive AND DmaChannelMask. - ClearDmaChannel [A: DmaChannelMask] Determine if any channels have completed, and update DmaActive. Clear the Active bits in DmaActive as indicated by DmaChannelMask. Re-enable the DmaComtroller with the new Active bits. Questions: - what if channel is active and a StartChannel is received? } ; Internal definitions: ChannelMask equ 0FH ;Mask of Channel field in Function/ChannelMask byte nChannelMask equ 0FFH-ChannelMask ;Complement of ChannelMask FunctionMask equ 0C0H ;Mask of Function field in Function/ChannelMask byte DisableDma equ 0 ;Dma Mode value to disable all channels ; ; PUBLIC Dma Subroutines. { Subroutine: StartCPDmaChannel [H,L: POINTER to DmaControlBlock]. Set up the address, count, Dma function of the CP channel. Enable the Dma Channel. Note: TCS and EW are set in Mode automatically. Assumption: Byte count is only 14 bits, i.e. high 2 bits are zero. NO DMA CHANNEL SHALL BE RESTARTED WITHOUT USING ReadDmaCompletion OR ClearDmaChannel TO ENSURE THE CHANNEL'S TERMINAL COUNT BIT IS OFF WHEN StartDmaChannel ACCESSES THE DMA CHIP. If the TC bit is on, the channel will not be restarted. On entry: A = function: DmaRead or DmaWrite HL = pointer to Dma Control Block (format described above). byte 0: Byte count low, byte 1: Byte count high, byte 2: Memory Address Low, byte 3: Memory Address high, } StartCPDmaChannel: MOV E,M ;E ← low byte count INX H ;Point to High count MOV D,M ;D ← High byte count DCX D ;Decrement for Dma controller ORA D ;OR in Dma function MOV D,A MOV A,E OUT DmaCh1Count ;Send low count MOV A,D OUT DmaCh1Count ;Send high count INX H ;Point to Low address MOV A,M ;E ← Memory Address Low OUT DmaCh1Addr ;Send low address INX H MOV A,M ;D ← Memory Address High OUT DmaCh1Addr ;Send high address {Now we are ready to start the DMA for the CP channel. When we send the command to the DMA controller, the low 4 bits of the command specify which channels to start. We must start the CP channel as well as any other channels that are currently busy. The problem is to know what channels are currently busy so we can send the proper command to the controller. DmaActive is the command we last sent to the DMA to start one or more channels. The low 4 bits of this command indicate which channels were last started. Some of these channels may have completed since then. In order to find out what channels have completed, we must read the Status Register. The low 4 bits of this register indicate what channels have completed. However, we cannot read this register and use this information to restart channels that have not completed, because a channel could complete in this interval. To leave a channel enabled is like pointing a loaded gun at our memory. So what me must do is to stop all channels except for the floppy channel before we read the Status Register. We must not stop the floppy channel if it is busy because it cannot afford the time.} RIM ANI InterruptEnbMsk ;A#0 => interrupts were enabled {If the floppy and RS232C are running at the same time, we must disable interrupts when DmaActive is being updated. The following op code will be a JMP whenever the floppy is NOT active. It will be JZ whenever the floppy IS active. If the floppy is active AND interrupts are enabled, we will disable interrupts.} FloppyActiveSwitch1: DB opJMP DW StartCPDmaChannelNoDisable {Come here if the floppy is running and interrupts are enabled.} DI {Set up A with the ModeSetRegister contents. If the floppy was busy, we will set the floppy active and turn all other channels off. If the floppy was not busy, we will turn off all channels.} LDA DmaActive MOV D,A ;Save DmaActive in D ANI DmaCh0Mask ;A ← 1 if floppy was busy OUT DmaMode ;Disable channels IN DmaStatus ;Read TerminalCount flags CMA ;A ← inverted TerminalCount Flags {Now A contains zero in bit positions of channels that have completed and ones in all other bit positions. We AND this with DmaActive. This gives us a command to restart all channels that need to be restarted. We then OR in the bit for the CP channel, send the command to the DMA, and save DmaActive.} ANA D ORI CPChannelMask OUT DmaMode ;Start Dma STA DmaActive ;Store back in DmaActive EI RET StartCPDmaChannelNoDisable: {Come here if the floppy is not running or if the floppy is running and interrupts are disabled.} DB opMVIA ;A ← DmaActive DmaActive: DB DmaModeExtra MOV D,A ;Save DmaActive in D ANI DmaCh0Mask ;A ← 1 if floppy was busy OUT DmaMode ;Disable channels IN DmaStatus ;Read TerminalCount flags CMA ;A ← inverted TerminalCount Flags {Now A contains zero in bit positions of channels that have completed and ones in all other bit positions. We AND this with DmaActive. This gives us a command to restart all channels that need to be restarted. We then OR in the bit for the CP channel, send the command to the DMA, and save DmaActive.} ANA D ORI CPChannelMask OUT DmaMode ;Start Dma STA DmaActive ;Store back in DmaActive RET ReadDmaCompletion: {Read Dma Status, and update DmaActive. Return updated ~DmaActive masked by DmaChannelMask. On entry: E = DmaChannel Mask On exit: A = ~DmaActive AND DmaChannelMask, condition code is set} RIM ANI InterruptEnbMsk ;A#0 => interrupts were enabled {If the floppy and RS232C are running at the same time, we must disable interrupts when DmaActive is being updated. The following op code will be a JMP whenever the floppy is NOT active. It will be JZ whenever the floppy IS active. If the floppy is active AND interrupts are enabled, we will disable interrupts.} FloppyActiveSwitch2: DB opJMP DW ReadDmaCompletionNoDisable {Come here if the floppy is running and interrupts are enabled.} DI LDA DmaActive ;D ← DmaActive MOV D,A IN DmaStatus ;Get the completed channels CMA ;Form complement mask of completed channels ANA D ;Clear channels in DmaActive STA DmaActive ;Store back in DmaActive EI CMA ANA E ;Mask channels of interest RET ReadDmaCompletionNoDisable: {Come here if the floppy is not running or if the floppy is running and interrupts are disabled.} LDA DmaActive ;D ← DmaActive MOV D,A IN DmaStatus ;Get the completed channels CMA ;Form complement mask of completed channels ANA D ;Clear channels in DmaActive STA DmaActive ;Store back in DmaActive CMA ANA E ;Mask channels of interest RET ClearDmaChannel: {Read the completed channels in DmaStatus, and remove from DmaActive. Remove the channels specified by DmaChannelMask and re-enable the Dma controller. Note that a hardware channel is automatically cleared at a terminal count condition. On entry: A = DmaChannel Mask} CMA ;Complement channel mask MOV E,A ;E ← Complemented mask of channels to be disabled RIM ANI InterruptEnbMsk ;A#0 => interrupts were enabled {If the floppy and RS232C are running at the same time, we must disable interrupts when DmaActive is being updated. The following op code will be a JMP whenever the floppy is NOT active. It will be JZ whenever the floppy IS active. If the floppy is active AND interrupts are enabled, we will disable interrupts.} FloppyActiveSwitch3: DB opJMP DW ClearDmaChannelNoDisable {Come here if the floppy is running and interrupts are enabled.} DI LDA DmaActive ANA E ;A ← mask of channels to be active MOV D,A ;D ← DmaActive ANI DmaCh0Mask OUT DmaMode ;Disable Dma IN DmaStatus ;Read Terminal Count Flags CMA ;A ← inverted Terminal Count Flags ANA D ;Clear completed channels in DmaActive OUT DmaMode ;Restart DMA STA DmaActive ;Store back in DmaActive EI RET ClearDmaChannelNoDisable: {Come here if the floppy is not running or if the floppy is running and interrupts are disabled.} LDA DmaActive ANA E ;A ← mask of channels to be active MOV D,A ;D ← DmaActive ANI DmaCh0Mask OUT DmaMode ;Disable Dma IN DmaStatus ;Read Terminal Count Flags CMA ;A ← inverted Terminal Count Flags ANA D ;Clear completed channels in DmaActive OUT DmaMode ;Restart DMA STA DmaActive ;Store back in DmaActive RET ; { Subroutine: StartFloppyChannel [H,L: POINTER to DmaControlBlock]. Set up the address, count, Dma function of the CP channel. Enable the Dma Channel. Note: TCS and EW are set in Mode automatically. On entry: A = function: DmaRead or DmaWrite HL = pointer to Dma Control Block (format described above). byte 0: Byte count low, byte 1: Byte count high, byte 2: Memory Address Low, byte 3: Memory Address high, } StartDmaChannel: StartFloppyChannel: MOV E,M ;E ← low byte count INX H ;Point to High count MOV D,M ;D ← High byte count DCX D ;Decrement for Dma controller ORA D ;OR in Dma function MOV D,A MOV A,E OUT DmaCh0Count ;Send low count MOV A,D OUT DmaCh0Count ;Send high count INX H ;Point to Low address MOV A,M ;E ← Memory Address Low OUT DmaCh0Addr ;Send low address INX H MOV A,M ;D ← Memory Address High OUT DmaCh0Addr ;Send high address {Now we are ready to start the DMA for the Floppy channel. When we send the command to the DMA controller, the low 4 bits of the command specify which channels to start. We must start the Floppy channel as well as any other channels that are currently busy. The problem is to know what channels are currently busy so we can send the proper command to the controller. DmaActive is the command we last sent to the DMA to start one or more channels. The low 4 bits of this command indicate which channels were last started. Some of these channels may have completed since then. In order to find out what channels have completed, we must read the Status Register. The low 4 bits of this register indicate what channels have completed. However, we cannot read this register and use this information to restart channels that have not completed, because a channel could complete in this interval. To leave a channel enabled is like pointing a loaded gun at our memory. So what me must do is to stop all channels before we read the Status Register.} LDA DmaActive MOV D,A ;Save DmaActive in D XRA A OUT DmaMode ;Disable channels IN DmaStatus ;Read TerminalCount flags CMA ;A ← inverted TerminalCount Flags {Now A contains zero in bit positions of channels that have completed and ones in all other bit positions. We AND this with DmaActive. This gives us a command to restart all channels that need to be restarted. We then OR in the bit for the CP channel, send the command to the DMA, and save DmaActive.} ANA D ORI DmaCh0Mask OUT DmaMode ;Start Dma STA DmaActive ;Store back in DmaActive RET END DmaSubs