{ File: [Iris]<WMicro>DLion>DmaSubs.asm
Modification History:
Dennis DEG 1-Sep-84 16:46:18: Added copyright notice.
Added copyright noticeJim JXF 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 RXO (June 17, 1980 1:48 PM)
}
{ Copyright (C) 1980, 1981, 1982 by Xerox Corporation. All rights reserved.}
; 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