www.delorie.com/djgpp/v2faq/faq147.html | search |
| Previous | Next | Up | Top |
Q: How do I access my peripheral card which is memory-mapped to an address between 640K and 1M?
Q: How can I read or change a value of one of the variables in the BIOS data area?
Q: How can I peek at an address whose far pointer I get from an INT 21h call?
_dos_ds
macro (defined on the go32.h
header file). This selector has base address of 0 and a limit of 1MB+64KB, so you can use it to access any address in the conventional memory, including the UMBs, but the relatively large limit
allows a buggy program to overwrite portions of DOS memory(Note: DJGPP v2.01 makes the limit of _dos_ds
be 4GB, which effectively disables memory protection when you use that
selector. However, since no memory outside the first 1MB is properly mapped into your program's address space without additional DPMI calls, and the DPMI host is then free to put memory-mapped
devices, such as Weitek I/O space or the linear frame buffer of an SVGA, on any address it sees fit, that huge limit is an unjustified security hole. DJGPP v2.02 will really be limited by
1MB+64KB.). The advantage of _dos_ds
is obviously that you don't have to create it, and that it is good for accessing every region in the first MByte range.
_dos_ds
. For example, here's a code snippet to set up a
selector which provides access to 32KB of text-mode video memory at 0xB800:0000
, courtesy of Bill Currie(Note: If you want to
decipher the 8-byte structure that is passed to __dpmi_set_descriptor
in this example, read the documentation of the __dpmi_get_descriptor
library function in the library
reference. This structure is the descriptor maintained by the processor for each protected-mode segment, such as those loaded into the CS and DS registers.):
int TxtVRAMSetupSelector (void) { static char selectorData[8] = { 0xff, 0x7f, 0x00, 0x80, 0x0b, 0xf3, 0x40, 0x00 }; int screenSelector = __dpmi_allocate_ldt_descriptors (1); if (__dpmi_set_descriptor (screenSelector, selectorData) < 0) abort (); return screenSelector; }The advantages of this method are that (a) you can set up the selector limit such that it only covers the memory region that you need, thus protection of the rest of memory is retained; and (b) you may set the base address to point to the beginning of the specific memory region you need to access, so that you don't have to add the base address for every access, making the access faster.
__dpmi_segment_to_descriptor
which is a wrapper
around that DPMI service. It is easier to use than the __dpmi_set_descriptor
function above, since you don't have to mess with the 8-byte descriptor buffer, but it always defines a 64KB
limit by default. Here is an example of code which gets a selector to access 64KB of video RAM beginning at 0xA000:0000
:
short video = __dpmi_segment_to_descriptor(0xa000);Note that descriptors created by this function should never be modified or freed. For this reason, you should use this function sparingly. For instance, if your program needs to examine various real mode addresses using the same selector, you should allocate a descriptor and change the base using the
__dpmi_set_segment_base_address
library function instead of using
__dpmi_segment_to_descriptor
to allocate separate descriptor for each address.
header file. You should convert any real-mode
far pointer segment:offset pair into a linear address (i.e., segment*16 + offset), and use _dos_ds
or any other selector which allows access to conventional memory, like this:
unsigned char value = _farpeekb(_dos_ds, segment * 16 + offset);For access to memory-mapped devices for which you have allocated a dedicated descriptor, use the selector of that descriptor instead of
_dos_ds
in the above example, and use the offset
into the on-board device memory as the offset. For example, the following snippet writes a value of 3 to the 10th dword of the device:
long lword = 3; _farpokel (selector, 9, lword);Use
_farpeekw
to peek at 16-bit shorts and _farpeekl
to peek at 32-bit longs. If you need to access several (non-contiguous) values in a loop, use the corresponding
_farnspeekX
functions which allow you to set the selector only once, as opposed to passing it with every call (but be sure the loop doesn't call any function that itself sets the
selector; see the library reference for more details).
There is a corresponding set of _farpokeX
and _farnspokeX
functions to poke (change the values of) such memory locations.
These functions have an advantage of emitting inline assembly code when you compile with optimizations, so they are very fast. See the library reference Info file for further details about these functions.
dosmemget
and dosmemput
library functions. They also require you to convert the segment:offset pair into a
linear address, but they don't need the conventional memory selector, as they can only be used to access the conventional memory (they use _dos_ds
internally).
Note that some memory-mapped peripheral devices might require 16-bit word accesses to work properly, so if dosmemXXX
yields garbled results, try dosmemXXXw
or set up a loop
which calls "farptr" functions.
_dos_ds
(e.g., created by one of the methods explained above), use the movedata
library function. It requires that you pass
a selector and an offset for both the memory-mapped address and for the buffer in your program's address space. Use the _my_ds()
function (note that it's a function,
not a variable!) to get the selector of any variable in your program, and use the address of the variable (cast to an int
) as its "offset" or linear address. movedata
is
fast because it moves by 32-bit longs, but be careful with its use when moving data to and from peripheral cards: some of them only support 8- or 16-bit wide data path, so moving data 4 bytes at a
time won't gain you much, and might even get you in trouble with some buggy BIOSes. The functions movedatab
and movedataw
are provided for moving by bytes and by 16-bit
words, respectively.
For example, here is a code snippet that combines one of the methods for allocating a descriptor for video RAM access with a call to movedata
to move a buffer to the graphics screen:
short video = __dpmi_segment_to_descriptor(0xa000); movedata(_my_ds(), buffer, video, 0, 320*200);
webmaster donations bookstore | delorie software privacy |
Copyright ⌐ 1998 by Eli Zaretskii | Updated Sep 1998 |
You can help support this site by visiting the advertisers that sponsor it! (only once each, though)