-------------------------
| DMA Tutorial |
| Revision 1.20 |
| Aug 01, 1994 |
| By Tom Marshall |
| 1:3407/12.2 (Fidonet) |
-------------------------
INTRODUCTION
============
I recently got into SoundBlaster programming, which requires DMA.
Information on DMA programming is extremely scarce, and it seems that
someone is always asking how to use it, so I decided to write a document
on using the DMA. None of the information in this document was obtained
from "hard" sources (books, tech sheets, etc.) Everything here was
gleaned from other text files. Some is tested, some is not. Some
information (especially on 16-bit channels) is extrapolated. If a
register or option is not explained fully, it is probably because the
information was copied and I don't know myself. Use this information at
YOUR OWN RISK! If you find any errors or typos, please let me know via
the 80XXX echo or netmail. I would like to thank the following people for
their help in my quest for DMA information:
James Vahn (1:346/25.3) - James keeps the 80XXX snippets, the source for
most of this information. If it weren't for him, I'd still be
pestering people for information!
Draeden of VLA (Unknown) - Draeden wrote a great article on DMA
programming with sample code. I hear he's put out some good stuff on
graphics, too.
Inbar Raz (2:403/123.5) - Inbar posted a message on 80XXX about two years
listing all the DMA ports. I filed it away for future use, and it
came in very handy as a supplement to Draeden's article.
Jim Roberts (1:301/40) - Jim wrote an article on SoundBlaster programming
which covers DMA transfers and includes sample code. His article also
explains how to use the mysterious Auto-Init DMA mode on the SB.
Coridon Henshaw (1:250/820) - Corrections and updates.
Bruce Wedding (1:106/4708) - Corrections and updates.
This document explains how to utilize the DMA channels on a standard AT
class machine. The AT maintains backward compatibility with the XT by
using one 8-bit DMA controller and one 16-bit DMA controller. The 8-bit
channels are 0-3 and the 16-bit channels are 4-7. Although no provision
is made in this document to explain DMA on the XT, programming the DMA
controller is similar between the two machines. The same ports are used
for the 8-bit channels; however the XT doesn't have any 16-bit channels.
The XT also uses DMA channels for different functions than the AT: the
only free DMA channel on the XT is 1. The following table illustrates
each channel's usage on both machines (corrections, please?):
Channel Size Usage in XT Usage in AT
------- ------ ------------------ ------------------
0 8-Bit Memory refresh * Free for use *
1 8-Bit Free for use * Free for use *
2 8-Bit Floppy controller Floppy controller
3 8-Bit HDD controller Free for use
4 16-Bit N/A Cascade, not avail
5 16-Bit N/A Free for use
6 16-Bit N/A Free for use
7 16-Bit N/A Free for use
* Also used for memory-to-memory transfers
DMA LIMITATIONS AND APPLICATIONS
================================
DMA transfer speed is significantly slower than manual transfers. There
are several reasons for this, including backward compatibility with the XT
and limited buss bandwidth. One text file I read (author unknown) said
that the theoretical maximum transfer speed is about 350k/sec on the 8-bit
channels. I would expect the 16-bit channels to transfer data twice as
fast as the 8-bit channels, but that's still not very fast by today's
standards.
DMA transfers are programmed using a page, offset, and length. These are
analogous to the DS, SI, and CX registers when using REP MOVSB. You don't
need a destination address because the peripheral "sees" the data on the
buss. The page can be thought of as a DMA segment register. It can be
set to any valid value, but it cannot change during a transfer. The
offset and length are both 16 bits. So there are two limits on DMA
transfers. First, the block of memory that is transferred cannot overflow
across pages. For the 8-bit channels, the pages start at physical 64k
intervals and each is 64k long. For the 16-bit channels, the pages start
at physical 64k intervals and each is 128k long (because the block size is
specified in words). Second, the size of the DMA transfer is limited to
the length of the page (64k or 128k).
With all these restrictions on DMA transfers, why use DMA at all? It's
quicker and easier to transfer information directly with the CPU. The
answer is that DMA transfers take place in the background, without CPU
intervention. While the DMA is transferring data, the CPU can continue
processing other information. Aside from standard usage (drive
controllers), the most common use for DMA is transferring data to and from
sound cards. It can also be used to transfer blocks of information from
one part of memory to another. For example, DMA could enable you to send
digitized sound to the sound card and update the graphics screen in the
background while the CPU is calculating the next frame for the screen.
DMA REGISTERS
=============
The DMA registers are accessed via I/O ports. In order to maintain
backward compatibility with the XT, the 16-bit registers are located in a
separate address range from the 8-bit channels. Since DMA is implemented
on the motherboard, all DMA ports are in the range 00h-FFh. The ports are
arranged as follows:
General Registers (8-bit)
=========================
Port Access Function
---- ------ --------
08h Rd/Wrt Command and Status Register
09h Wrt Request Register
0Ah Wrt Single Mask Register
0Bh Wrt Mode Register
0Ch Wrt Clear Flip/Flop Register
0Dh Wrt Master Reset Register
0Eh Wrt Master Enable Register
0Fh Wrt Master Mask Register
General Registers (16-bit)
==========================
D0h Rd/Wrt Command and Status Register
D2h Wrt Request Register
D4h Wrt Single Mask Register
D6h Wrt Mode Register
D8h Wrt Clear Flip/Flop Register
DAh Wrt Master Reset Register
DCh Wrt Master Enable Register
DEh Wrt Master Mask Register
As you can see, the registers are identical for both DMA controllers
(8-bit channels 0-3 and 16-bit channels 4-7). Each register is described
once in the following list. Some functions may not apply to the 16-bit
controller, (which? is 16bit mem-mem allowed?). As I stated earlier, I
don't have a lot of information on the 16-bit channels. When selecting a
channel in binary for the 16-bit controller, subtract 4 from the channel
number. For example, channel 5 would be 01b. You MUST disable a channel
before you can program it.
Bit(s) Function
-------- ------------------------------------------------------
7 1 = DACK (DMA Acknowledge) sensing active high
0 = DACK sensing active low (default, do not change) *
6 1 = DRQ (DMA Request) sensing active low
0 = DRQ sensing active high (default, do not change) *
5 1 = Extended write mode ** \ Irrelevant
0 = Late write mode ** / if b3=1
4 1 = Rotating priority
0 = Fixed priority (default)
3 1 = Compressed timing (???) \ Irrelevant
0 = Uncompressed timing (???) / if b0=1
2 1 = Controller disabled
0 = Controller enabled
1 1 = Enable channel 0 (4?) address hold ***
0 = Disable channel 0 (4?) address hold ***
0 1 = Enable memory-to-memory transfer (8-bit only?)
0 = Disable memory-to-memory transfer (8-bit only?)
* b7,b6 require hardware modifications for usage.
** b5 used for mem-mem xfers. [CH]
*** If set, channel 0 will not increment/decrement its address.
This can be used to set a block of memory to one value with a
mem-mem transfer, similar to REP STOSB.
Status Register (Port 08h, D0h - Read)
======================================
Bit(s) Function
-------- ------------------------------------------------------
7 1 = Channel 3/7 has a request pending
6 1 = Channel 2/6 has a request pending
5 1 = Channel 1/5 has a request pending
4 1 = Channel 0/4 has a request pending
3 1 = Channel 3/7 at terminal count (transfer done)
2 1 = Channel 2/6 at terminal count (transfer done)
1 1 = Channel 1/5 at terminal count (transfer done)
0 1 = Channel 0/4 at terminal count (transfer done)
If a request is pending, it means that some device has asserted
that channel's DRQ line on the bus and is requesting a DMA
transfer. These bits don't really have any use other than code
testing. [CH]
Bit(s) Function
-------- ------------------------------------------------------
76543 Unused
2 1 = Set request
0 = Reset request
10 Channel number (binary)
If the request bit is set, the channel will begin transferring
regardless of the DREQ state. Used to start DMA transfers via
software, such as mem-mem transfers. [BW]
Single Mask Register (Port 0Ah, D4h - Write)
============================================
Bit(s) Function
-------- ------------------------------------------------------
76543 Unused
2 1 = Set mask (disable channel)
0 = Reset mask (enable channel)
10 Channel number (binary)
* Demand mode: "Normal mode for ... slow devices." [CH]
Single mode: I use this for the SB. Description, anyone?
Block mode: "Everything else uses block mode." [CH]
Supposedly used for mem-mem transfers also. (?)
Cascade mode: Cascade mode is used ONLY for channel 4. BIOS
programs channel 4 at boot for cascading; don't mess with it
or you will disable all the high DMA channels!
Clear F/F Register (Port 0Ch, D8h - Write)
==========================================
Also called "Clear Byte Pointer Register". Any write to this
register resets the internal pointers for the 16-bit registers.
The 16-bit registers accept two bytes in sequence, low byte first.
By writing to this register, you ensure that the program and the
DMA controller are in sync. This is useful because some errant
program may inadvertently write a single value to a 16-bit DMA
register, getting it "out of sync".
Master Reset Register (Port 0Dh, DAh - Write)
=============================================
Any write to this register resets the controller and all four of
its channels.
Master Enable Register (Port 0Eh, DCh - Write)
==============================================
Any write to this register enables all four channels on the
controller.
Master Mask Register (Port 0Fh, DEh - Write)
============================================
Allows all channels to be programmed (enabled or disabled) at the
same time.
Page Registers (Write only)
===========================
The page registers indicate the "page" of memory for a transfer.
A page is a block of 64k (or 128k) that is used as a base for the
transfer, much as a segment register is used as a base for memory
addressing. The 8-bit channels only use the lower 4 bits of the
page value, so they are limited to the first meg of memory. The
16-bit channels use all 8 bits of the page value. In addition,
the 16-bit pages are 128k long since the transfers are in words.
So the 16-bit channels may access the first 32 megs of memory. In
286 systems, there are only 24 address lines, so bit 7 should
always be 0. In 386+ systems, bit 7 may or may not be used,
depending on the motherboard design.
Offset Registers (Write/Read)
=============================
Writing to the offset registers sets the starting offset for a
transfer. Reading from the offset registers gives the current
offset in an active DMA transfer. These registers are 16 bits
wide for both 8-bit and 16-bit channels. For 8-bit channels, the
offset is indicated directly. For 16-bit channels, the offset is
indicated in words. This means that the value programmed into the
offset register must be half the actual offset, and the transfers
must be word aligned. The offset registers use standard Intel
word ordering; send/read the low byte of the offset first,
followed by the high byte. It's a good idea to reset the byte
pointer flip/flop before reading or writing to this register to
ensure that your program and the controller are in sync. The
physical address for a DMA transfer is:
Block Size / Countdown Registers (Write/Read)
=============================================
Writing to the block size registers sets the DMA block length.
Reading from the countdown registers gives the remaining block
size MINUS ONE for an active DMA transfer. When a transfer is
complete, the countdown register is equal to -1 (0FFFFh). Note
that both registers reside at the same port address; the one
selected depends on whether you are reading or writing. These
registers indicate the transfer length in bytes (8-bit) or words
(16-bit). This value is the actual block length MINUS ONE. This
allows a full 64k (or 128k) block to be transferred at a time.
These are 16-bit registers, so the maximum transfer size for the
8-bit channels is 64k, and the maximum transfer size for the
16-bit channels is 128k.
The order in which you program the registers doesn't really matter, as
long as you disable the channel first and reset the Byte F/F before
programming the 16-bit registers. I have found that a convenient method
for DMA programming goes something like this:
1. Disable channel
2. Reset Byte F/F
3. Set mode
4. Set page
5. Set offset
6a. Set block length
6b. Send block length to peripheral
7. Enable channel
8. Program peripheral to receive DMA
SAMPLE CODE
===========
The following code segments demonstrate how to use DMA. I can't figure
out the memory-memory transfers, but I've included code that should work
if given the proper Command Register byte. This code will run under TASM
in Ideal mode as listed. If you are using MASM (or MASM mode), reverse the
PROC and ENDP statements as indicated. If you are using any other
assembler that doesn't support MASM syntax, you're on your own. These
routines use multiple bit shifts, so you must enable 286+ processing (with
P286N or .286).
;***********************************************************************
;* DMA8 - Setup an 8-bit DMA transfer. *
;* Entry: AH = DMA channel (0-3) *
;* AL = Mode (see below) *
;* ES:DI = 8-bit DMA normalized transfer address *
;* CX = Block length (64k=0000h) *
;* Exit : CX = Block length minus one *
;* CF = 1 : Page overflow, transfer not attempted *
;* CF = 0 : Transfer in progress *
;* Regs : AX, BX, DX modified *
;* Note : Common modes for DMA programming are: *
;* 44h : Single Write - Write block to memory (from buss) *
;* 48h : Single Read - Read block from memory (to buss) *
;* 54h : Auto-Init Write - Write block to memory with looping *
;* 58h : Auto-Init Read - Read block from memory with looping *
;***********************************************************************
PROC DMA8 ;"DMA8 PROC" for MASM
and ah,3 ;Ensure channel correct
and al,NOT 3 ;Ensure mode correct
or ah,al ;Combine them
dec cx ;Fixup length
mov dx,di ;Check for overflow
add dx,cx
jo @@BadAddr
mov al,ah ;Disable channel
and al,3
or al,100b
out 0Ah,al
mov dx,es ;Load DX and AX with segment
mov ax,dx ; for manipulation
shr dx,12 ;Page to DX
shl ax,4 ;Segment fixup to AX
add di,ax ;Add to offset
adc dx,0 ;Overflow bumps page
mov es,dx ;Save page in DS
pop dx
pop ax
ret
ENDP ;"NormDMA8 ENDP" for MASM
;***********************************************************************
;* DMA8Stat - Check status of 8-bit DMA transfer. *
;* Entry: AH = DMA channel *
;* Exit : CX = Bytes remaining MINUS ONE or -1 if done *
;* Regs : AL modified *
;* Note : To check 16-bit DMA, change the port to Channel*4 + 1 + C0h. *
;* CX will be returned as WORDS remaining. *
;***********************************************************************
PROC DMA8Stat ;"DMA8Stat PROC" for MASM
push dx