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
__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:
This method has a disadvantage of using conventional memory which is at a premium, and is therefore generally ill-suited for large DMA buffers.
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
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
movedataand "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.