Node:DMA,
Previous:Inline Asm,
Up:Low-level
Q: How do I use DMA with DJGPP programs?
Q: I want to use DMA, but I don't know how to get the physical
address of the buffer I allocate for that purpose.
A: The main problem in using DMA with DJGPP is how to get the
physical address of a buffer, which is required to program the DMA
controller. In protected-mode environment, memory addresses that your
program manipulates are actually offsets from the base address of the
data segment. You can obtain the base address of the data segment by
calling the __dpmi_get_segment_base_address
library function and
add it to the address of your buffer, but the resulting address is a
logical address, translated into a physical address by the
memory-mapping unit which is part of the CPU.
You have several alternatives to get the physical address of your buffer:
__dpmi_allocate_dos_memory
.
This method has a disadvantage of using conventional memory which is at a premium, and is therefore generally ill-suited for large DMA buffers.
EMM386
or QEMM
, to run, since only memory
managers and Windows support the VDS API (except that Windows/NT
doesn't, or so I'm told). In other words, if you use VDS, your program
won't work on a system where CWSDPMI38 is used as the DPMI server, allocating memory by
raw XMS calls or via HIMEM.
The following snippet tests bit 5 of the BIOS data area at 0040:007B, to see whether the VDS API is supported:
#include <go32.h> #include <sys/farptr.h> int vds_available = (_farpeekb (_dos_ds, 0x0047b) & 0x20) != 0;
(However, an explicit call to the VDS API, after the test above shows that the bit is set, is a more reliable way to detect the VDS support.)
To use VDS, you allocate a buffer, then use the Lock DMA Buffer Region function (AX=8103h) to get its physical address. Then you call the Disable DMA Translation function (AX=810Bh), program the DMA controller with the physical address returned by the Lock DMA Region function, and start the transfer. After the transfer, you need to call the Enable DMA Translation (AX=810Ch) and Unlock DMA Buffer Region (AX=8104h) functions.
Alternatively, you could use the Request DMA Buffer and Release DMA
Buffer services, but then you will need to copy the data to and from the
DMA buffer (e.g. using the movedata
function).
The VDS method is convenient when your program needs to work in several different environments, such as both DOS and Windows, and if you don't want to waste the conventional memory. Experience shows, however, that VDS is inconvenient for buffers larger than 128KB, because many implementations of VDS fail for large buffers, in particular in plain DOS. If you need large DMA buffers, use the XMS method, described next.
__dpmi_simulate_real_mode_procedure_retf
(don't forget to zero
out the SS and SP registers!) passing it the address you got
from the 2Fh/4310h call. You need to allocate a buffer with XMS
function 09h, then lock that buffer with function 0Ch; the last call
returns the 32-bit physical base address of the allocated block, with
which you program the DMA controller. You can then map the physical
address to a linear address and build a descriptor that spans the entire
buffer, in the same manner as you would map a memory-mapped device, see
mapping physical address to linear address. This gives you a selector which you can
use to copy data between your program and the DMA buffer with
movedata
and "farptr" functions.
The XMS method is especially suited to very large DMA buffers, like 2MB (these obviously cannot be allocated in conventional memory). It is also supported by more system configurations used to run DJGPP, since even HIMEM supports XMS.