18. Low-level DOS/BIOS and Hardware-oriented Programming

18. ஢ DOS / BIOS  ⭮ - ਥ஢ ணࠬ஢

********************************************************

********************************************************





This chapter sheds some light on a few aspects of writing DJGPP programs which interact with hardware or use interrupts.

  ஫ ᢥ  ᪮쪮 ᯥ⮢ ஢ ணࠬ DJGPP,    묨 ।⢠  ᯮ 뢠.





18.1 Got "Unsupported INT 0xNN" calling `int86'

18.1 祭 " ন INT 0xNN " 뢠 "int86"

===============================================

===============================================





**Q*: Why does my program crash with "Unsupported DOS request 0xNN" or "Unsupported INT 0xNN" when I call `int86' or `intdos' functions to invoke a software interrupt?*

** Q*: 祬  ணࠬ 蠥  ਩ ⪠ " ন DOS, 訢 0xNN "  " ন INT 0xNN "   뢠 "int86"  " 㭪樨 intdos', ⮡ 맢 ணࠬ 뢠? *





*A* :  Calling real-mode DOS or BIOS services from protected-mode programs requires a switch to real mode, so the `int86' family of functions in the DJGPP library should reissue the INT instruction after the mode switch. However, some services require pointers to memory buffers.  Real-mode DOS/BIOS functions can only access buffers in conventional memory, so `int86' has to move data between your program and low memory to transparently support these services.  But this means it should know about all these services to perform these chores correctly, because each service has its own layout and size of the buffer(s).  While `int86' supports many of these services, it doesn't support all of them.  The supported functions are listed in the library reference.  See int86 library reference in "libc.a reference", or point your Web browser to http://www.delorie.com/djgpp/doc/libc-2.01/libc_411.html#SEC411.  For those it doesn't support, you will have to call the `__dpmi_int' library function instead.  It is also documented in the library reference, See __dpmi_int in "libc.a reference", or point your Web browser to http://www.delorie.com/djgpp/doc/libc-2.01/libc_208.html#SEC208. `__dpmi_int' requires that you set up all the data as required by the service you are calling, including moving the data to and from low memory (See how to use buffers with DOS/BIOS services in Section 18.2, below).

*A*: 맮 ॠ쭮 ० DOS  ࢨ BIOS  ணࠬ  饭 ० ॡ ४⥫  ॠ쭮 ०, ⠪ "int86" ᥬ⢮ 㭪権  ⥪ DJGPP,   INT  ᫥ ४祭 ०. ,  㣨 ॡ 㪠⥫   .  ० 㭪樨 DOS/BIOS  ⮫쪮   ࠬ  ⠭⭮ , ⠪ "int86",  ६   襩 ணࠬ   , ⮡ ন  㣨.   砥  㭪   ⭮⥫쭮   , ⮡ 믮  ࠡ ࠢ쭮, ⮬   㦨  ᮡ⢥ ࠧ饭  ࠧ  ().   ६  "int86" ন    ,   ন   . ᯥ稢 㭪樨 ᫥  筮 뫪. . int86  뫪  " libc.a 뫪 ",  㪠   ᬮ   http: // www.delorie.com/djgpp/doc/libc-2.01/lib c_411. html * SEC411.     ন,  㤥  맢 " __ dpmi_int ' 筠 㭪  ⮣.  ⠪ ॣ஢  筮 뫪, . __ dpmi_int  " libc.a 뫪 ",  㪠   ᬮ   http: // www.delorie.com/djgpp/doc/libc-2.01/lib c_208. html * SEC208. " __ dpmi_int ' ॡ, ⮡  ⠭    ॡ 㦨, ஥  뢠,  ६饭       (.,  ᯮ짮   㣠 DOS / BIOS   18.2, ).





18.2 How to use buffers with DOS/BIOS services

18.2,  ᯮ짮   㣠 DOS / BIOS

==============================================

==============================================





**Q*: I want to call a DOS/BIOS function which requires a pointer to a buffer in, e.g. `ES:DI' (or any other) register pair.  How do I get the segment to put into the `ES' register?*

** Q*:   뢠 㭪 DOS/BIOS,  ॡ 㪠⥫   , ਬ "ES:DI" (  㣮)  ॣ.    ᥣ, ⮡   " ES' ॣ? *





*A* :  If you use `int86x' or `intdosx' for a DOS or BIOS function supported by them, then just put the address of your buffer into the register which expects the offset (`regs.x.di') and forget about the segment.  These functions are processed specially by the library, which will take care of the rest.

*A*: ᫨  ᯮ "int86x"  "intdosx"  㭪樨 DOS  BIOS, ᯥ稢 ,  ⮫쪮   襣   ॣ,   ᬥ饭 ("regs.x.di")  뢠 ⭮⥫쭮 ᥣ.  㭪樨 ᤥ ⠪   ᠬ     ⪥.





If you call `__dpmi_int', then you must put into that register pair an address of some buffer in *conventional* memory (in the first 1 MByte).  If the size of that buffer doesn't have to be larger than the size of transfer buffer used by DJGPP (at least 2KB, 16KB by default), then the easiest way is to use the transfer buffer.  (Library functions don't assume its contents to be preserved across function calls, so you can use it freely.)  That buffer is used for all DOS/BIOS services supported by DJGPP, and it resides in conventional memory.  DJGPP makes the address and the size of the transfer buffer available for you in the `_go32_info_block' external variable, which is documented the library reference.  Check the size of the buffer (usually, 16K bytes, but it can be made as small as 2KB), and if it suits you, use its linear address this way:

᫨  뢠 " __ dpmi_int ',      ॣ     ண   *᭮*  ( ࢮ 1 ). ᫨ ࠧ ⮣    祬 ࠧ  ।, ᯮ㥬 DJGPP ( ࠩ  2, 16  㬮砭),  ᠬ ⮩ ᯮᮡ ⮨  ⮬, ⮡ ᯮ짮  ।. ( 㭪樨  ਭ,  ᮤঠ ࠭ ४ 㭪樮 饭, ⠪    ᯮ짮  ᢮.)   ᯮ    DOS / BIOS, ᯥ稢 DJGPP,   ﭭ 室  ⠭⭮ . DJGPP    ࠧ  ।, 㯭    "_go32_info_block" 譥 ६,  ॣ஢  筠 뫪. ஢ ࠧ  (筮, 16,     ᤥ ᥣ 2),  ᫨  室 , ᯮ    ⮬ ᯮᮡ:





dpmi_regs.x.di =

dpmi_regs.x.di =

_go32_info_block.linear_address_of_transfer_buffer & 0x0f; dpmi_regs.x.es =

_go32_info_block.linear_address_of_transfer_buffer & 0x0f; dpmi_regs.x.es =

(_go32_info_block.linear_address_of_transfer_buffer >> 4) & 0xffff;

(_go32_info_block.linear_address_of_transfer_buffer >> 4) & 0xffff;





For your convenience, the header file `<go32.h>' defines a macro `__tb' which is an alias for `_go32_info_block.linear_address_of_transfer_buffer.'

 襣 㤮⢠  䠩  " < go32. h > ' । ப " __ tb ',   ᥢ  " _go32_info_block.linear_address_of_transfer_buffer. '





If the size of the transfer buffer isn't enough, you will have to allocate your own buffer in conventional memory with a call to the `__dpmi_allocate_dos_memory' library function.  It returns to you the segment of the allocated block (the offset is zero).  If you only need a small number of such buffers which can be allocated once, then you don't have to worry about freeing them: they will be freed by DOS when your program calls `exit.'

᫨ ࠧ  ।  祭,  㤥  ।  ᮡ⢥   ⠭⭮   饭  " __ dpmi_allocate_dos_memory ' 筠 㭪.  頥  ,  ᥣ ।  (ᬥ饭 - ). ᫨  ⮫쪮 㦤   ᫥ ⠪ ஢,    ।,  ࠧ,      ⭮⥫쭮 ᢮ :   ᢮ DOS,   ணࠬ 뢠 "室".





For bullet-proof code, you should test the size of the transfer buffer at runtime and act accordingly.  This is because its size can be changed by the `STUBEDIT' program without your knowledge (however, it can never be less than 2KB, the size of the stub, because memory used by the stub is reused for the transfer buffer).

 㫥஡ ,   ஢ ࠧ  ।  ६ 믮  ⢨ ᮮ⢥⢥.  - , ⮬  ࠧ    ணࠬ STUBEDIT  㢥 (,       祬 2, ࠧ 誨, ⮬  , ᯮ㥬 誮 ⭮ ᯮ   ।).





18.3 How to call software interrupt functions

18.3  뢠 㭪樨 ணࠬ 뢠

=============================================

=============================================





**Q*: My program crashes/doesn't do what it should when I call `__dpmi_simulate_real_mode_interrupt.'*

** Q*:  ணࠬ ௨  /  ࠡ⠥,   뢠 " __ dpmi_simulate_real_mode_interrupt. '*





*A* :  You should zero out some of the fields of the `__dpmi_regs' structure before you call that function.  Random values in these fields can cause your program to behave erratically.  The fields in point are `SS', `SP' and `FLAGS.'  When `SS' and `SP' are zeroed, the DPMI host will provide a stack for the interrupt handler.  This stack is locked and is 4KB-long for any handling done in protected mode (such as real-mode callbacks), and at least 512 bytes in size for interrupts reflected into real mode.  This is usually enough, but sometimes you'll need to use your own, larger stack, e.g., if you expect interrupts to nest, or if your handler needs a lot of stack space. (The DPMI spec indicates that you should *not* use the default stack if your procedure/interrupt handler uses more that 60 bytes, or 1/8 of the total stack space available by default.)  In these cases you should point `SS' and `SP' to a larger buffer which is in conventional memory (possibly part of the transfer buffer).

*A*:   㫨 ᭠㦨     `__ dpmi_regs' ०, 祬  뢠  㭪. 砩 祭     ⠢  ணࠬ  ᥡ  .   窠 - " SS', "SP"  "FLAGS".  " SS'  " SP '  ﬨ, DPMI  㤥 ᯥ稢 ⥪  ணࠬ ࠡ⪨ 뢠.  ⥪ ஢   4KB   ࠡ稪, 믮  饭 ० (⠪  맮 ॠ쭮 ०),   ࠩ  512 ⮢  ࠧ  뢠, ࠦ  ॠ ०.  - 筮 筮,    㤥  ᯮ짮  ᮡ⢥ 訩 ⥪, ਬ, ᫨  ,  뢠 ,  ᫨  ࠡ稪 㦤  襬 ⥪. (DPMI ᯥ䨪樨 㪠뢠,     ᯮ짮   㬮砭 ⥪, ᫨   楤 / ࠡ稪 뢠 ᯮ  祬 60 ⮢,  1/8 饣 ࠧ ⥪, 㯭  㬮砭.)      㪠 " SS'  " SP '  訩 ,  室  ⠭⭮  (   ।).





If `SS:SP' isn't zero, it will be used as the address of the stack for the interrupt handler, so if it points to a random location, your program will most certainly crash.  A non-zero `FLAGS' field can also make the processor do all kinds of weird things (e.g., imagine that the single-step or the debug bit is set!).

᫨ "SS:SP"  ,   㤥 ᯮ짮   ⥪  ணࠬ ࠡ⪨ 뢠 ⠪, ᫨  㪠뢠  ந쭮 ᯮ,  ணࠬ 㤥  ਩. 㫥  "FLAGS"  ⠪ ⠢      ᢥ⢥  (ਬ, ࠦ  蠣   ⫠ ⠭!).





If you don't have any reason to set `SS:SP' to a user-defined stack, it's easier to call the `__dpmi_int' library function, which zeroes out the stack pointer and the `FLAGS' fields for you (and also doesn't force you to type long function names!).

᫨    稭  ⠭ "SS:SP"  ।塞 짮⥫ ⥪,  맢 " __ dpmi_int ' 筠 㭪,  ᠬ  㪠⥫ ⥪   "FLAGS"   ( ⠪  㦤 , ⮡    㭪樨!).





18.4 How to move data between your program and conventional memory?

18.4  ६   襩 ணࠬ  ⠭⭮ ?

===================================================================

===================================================================





**Q*: How can I move data between my program and the transfer buffer?*

** Q*:    ६    ணࠬ  ஬ ।? *





**Q*: How do I access my peripheral card which is memory-mapped to an address between 640K and 1M?*

** Q*:       ਩ ,   ⮡ࠦ   640  1M? *





**Q*: How can I read or change a value of one of the variables in the BIOS data area?*

** Q*:       祭   ६    BIOS? *





**Q*: How can I peek at an address whose far pointer I get from an INT 21h call?*

** Q*:    ᬮ  , 祩  㪠⥫    INT 21h 饭? *





*A* : Usually, memory-mapped devices or absolute addresses below 1MB mark are outside your program's address space, so you cannot access them directly. "Direct access", when you just dereference a pointer, means in DJGPP that you use your program's `DS' selector, and all the addresses are offsets relative to the base of that selector.  So first, you will need a special selector that will allow you to access your device or absolute address.  There are several methods you can get such a selector:

*A*: 筮, ன⢠  ⮡ࠦ   ᮫   ⪨ 1 -  ᭮ ࠭⢠ 襩 ணࠬ, ⠪        ।⢥. "אַ ",   ⮫쪮 ࠧ묥뢠 㪠⥫, ᯮᮡ  DJGPP,   ᯮ  ணࠬ " ᥫ DS,    - ᬥ饭 ⭮⥫쭮 ᭮ ⮣ ᥫ.  ᭠砫,  㤥 㦤  ᯥ樠쭮 ᥫ,      襬 ன  ᮫⭮ .  ⤥ ⮤,    ⠪ ᥫ:





* Use the selector that DJGPP creates for itself to access conventional memory.  DJGPP makes this selector available to you via the `_dos_ds' macro (defined on `<go32.h>' header file).  This selector has base address of 0 and a limit of 1MB, so you can use it to access any address in the conventional memory, but the relatively large limit allows a buggy program to overwrite portions of DOS memory.  (Original release of 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.) 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.

* ᯮ짮 ᥫ,  DJGPP ᮧ  ᥡ  饭  ⠭⭮ . DJGPP   ᥫ, 㯭  १ ப `_dos_ds' (।  " < go32. h > ' 䠩 ).  ᥫ    0  ࠭祭 1, ⠪    ᯮ짮 , ⮡      ⠭⭮ ,  ⭮⥫쭮 让   ணࠬ 뢠    DOS. (ࢮ砫  DJGPP v2. 01  ࠭祭 " _dos_ds' 4GB,  ⢨⥫쭮 ⪫砥     ᯮ  ᥫ. , ⠪     ࢮ  ࠢ쭮 ⮡ࠦ  ᭮ ࠭⢮ 襩 ணࠬ  ⥫ 饭  DPMI,  DPMI  ⥬   ன⢠  ⮡ࠦ , ⨯ Weitek I/O    ࠦ SVGA,   ,   ⮣ ਣ   ஬  - ᭮   ᥪ⭮.) २⢮ " _dos_ds' - 祢,     ᮧ ᢮ ਯ,    訬  㯠   祩   1.





* Create your own selector that spans only the region of memory that you want to access, and use that selector instead of `_dos_ds'.  For example, here's a code snippet to set up a selector which provides access to 64KB of text-mode video memory at `0xB800:0000', courtesy of Bill Currie <bill_currie@blackmagic.tait.co.nz>:

*   ᮡ⢥ ᥫ,  墠뢠 ⮫쪮   ,  ன   ,  ᯮ짮  ᥫ  " _dos_ds'. ਬ,  뢮 , ⮡ ⠭ ᥫ,  ᯥ稢   64    ⥪⮢ ०  " 0xB800:0000 ',  Bill Currie < bill_currie blackmagic.tait.co.nz >:





int TxtVRAMSetupSelector (void) {

int TxtVRAMSetupSelector (void) {

static char selectorData[8] = {

static char selectorData[8] = {

0xff, 0xff, 0x00, 0x80,

0xff, 0xff, 0x00, 0x80,

0x0b, 0xf3, 0x40, 0x00

0x0b, 0xf3, 0x40, 0x00

};

};

int screenSelector = __dpmi_allocate_ldt_descriptors (1); if (__dpmi_set_descriptor (screenSelector, selectorData) < 0) abort (); return screenSelector;

int screenSelector = __dpmi_allocate_ldt_descriptors (1); if (__dpmi_set_descriptor (screenSelector, selectorData) < 0) abort (); return screenSelector;

}

}





The advantages of this method are that (1) 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 (2) 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.  For details about the contents of the 8-byte selector descriptor, see the documentation of the `__dpmi_get_descriptor' function in the library reference Info file.

२⢠ ⮣ ⮤  ⮬  (1),   ⠭ ᥫ,  ࠭稢 室  ,  ன  㦤, ⠪ ࠧ  ⠫쭮   ࠭;  (2)   ⠭  , ⮡ 㪠  砫 ᯥ᪮  ,    , ⠪, ⮡         㯠,   ॥.  ஡⥩ ⭮⥫쭮 ᮤঠ 8-⮢ ᥫ୮ ⥫, . 㬥 " __ dpmi_get_descriptor ' 㭪  筮 뫪 䠩 ଠ樨.





* Use the DPMI service which creates a selector to access a specific real-mode segment address.  The DJGPP library has a function `__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':

* ᯮ짮 DPMI ࢨ,  ᮧ ᥫ, ⮡   ᯥ᪮  ᥣ ॠ쭮 ०. ⥪ DJGPP  㭪 " __ dpmi_segment_to_descriptor ',   窮  ⮣ DPMI ࢨ.   ᯮ짮 祬 㭪 , ⠪      ᯮ冷  8-⮢ ਯ ஬,   ᥣ । ࠭祭 64  㬮砭.  ਬ ,  砥 ᥫ, ⮡   64  - , 稭饩  "0xA000:0000":





short video = __dpmi_segment_to_descriptor(0xa000);

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.

 ,  ⥫, ᮧ ⮩ 㭪樥       ᢮.  ⮩ 稭,   ᯮ짮  㭪 . ਬ, ᫨  ணࠬ  ᫥ ࠧ   ॠ쭮 ० ᯮ   ᠬ ᥫ,   । ⯮   ᯮ짮  " __ dpmi_set_segment_base_address' 筮 㭪楩  ⮣, ⮡ ᯮ짮 " __ dpmi_segment_to_descriptor ', ⮡ । ⤥ ਯ   .





Once you have a selector, you can use one of three methods to access your absolute addresses using that selector:

᫨   ᥫ,   ᯮ짮    ⮤ 饭  訬 ᮫ ᠬ, ᯮ騬  ᥫ:





* If you want to access a byte, a 16-bit word, or a 32-bit double word, use the "far pointer" functions declared on the `<sys/farptr.h>' 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:

* ᫨     , 16-ࠧ來 ᫮,  32-ࠧ來  ᫮, ᯮ "  㪠⥫ " 㭪樨,   " <sys/farptr.h> ' 䠩 .   ८ࠧ  㪠  ॠ쭮 ०   segment:offset   "   " ( , segment*16 + ᬥ饭),  ᯮ짮 " _dos_ds'   㣮 ᥫ,     ⠭⭮ ,  ⮬:





unsigned char value = _farpeekb(_dos_ds, segment*16 + offset);

unsigned char value = _farpeekb(_dos_ds, segment*16 + offset);





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).

ᯮ "_farpeekw", ⮡ ᬮ  16-ࠧ來  "_farpeekl", ⮡ ᬮ  32-ࠧ來 . ᫨     ᪮쪨 祭 (騬  ᪮쪨 ᬥ ⪮)  横, ᯮ 㭪樨 騥  _farnspeekX,    ⠭ ᥫ ⮫쪮  ࠧ,  ⨢ 宦 ⮣   饭 ( 㡥,   横  뢠  㭪,  ।⢥ ⠭ ᥫ; .  뫪  襣 ⢠ ஡⥩).





There is a corresponding set of `_farpokeX' and `_farnspokeX' functions to poke (change the values of) such memory locations.

 㭪樨 騥  ஬  㭪権 `_farpokeX' and `_farnspokeX' ⮡   設  ( 祭) ⠪ ᯮ .





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.

 㭪樨  २⢮ ⠢  ஥ ᥬ୮ ,     ⨬樥, ⠪   祭 . .  뫪 䠩 ଠ樨  쭥 ஡⥩ ⭮⥫쭮  㭪権.





* If you need to access more than 4 contiguous bytes, use `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).

* ᫨      祬 4 뢭 ⠬, ᯮ "dosmemget"  "dosmemput"  㭪樨.  ⠪ ॡ, ⮡ , ⮡ ८ࠧ segment:offset   ,    㦤  ⠭⭮ ᥫ , ᪮   ⮫쪮 ᯮ짮, ⮡   ⠭⭮  ( ᯮ " _dos_ds' ७).





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 "farptr" functions.

     ਩ ன⢠  ⮡ࠦ    ॡ  16 - 來 㭪樨 㯠 , ⮬ ᫨ dosmemXXX   ந ᪠ १ ஡ `dosmemXXXw'  "farptr" 㭪樨.





* For moving buffers to selectors other than `_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 conventional memory 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.

*  ६饭 ஢  ᥫ 㣨 祬 " _dos_ds' (ਬ, ᮧ   ⮤, ᭥ ), ᯮ " movedata '  㭪. ॡ, ⮡  । ᥫ  ᬥ饭,   ⠭⭮       ᭮ ࠭⢥ 襩 ணࠬ. ᯮ 㭪 `_my_ds' ( ,   - *㭪 *,   ६!) ⮡  ᥫ  ६  襩 ணࠬ,  ᯮ  ६ (ਢ  "int")  "ᬥ饭"   . "Movedata" , ⮬   ६頥 32-ࠧ來 longs,   ⥫  ᯮ짮  ६饭     ਩ :    ⮫쪮 ন 8-  16-ࠧ來  ய᪠ , ⠪ ६   4  ६ 㤥 訡,   맢 㤭 BIOS. 㭪樨 "movedatab"  "movedataw" ।ᬮ७,  ६饭 ⠬  16-ࠧ來묨 ᫮, ᮮ⢥⢥.





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:

ਬ,  뢮 ,  ꥤ   ⮤  । ⥫   㯠    饭  "movedata", ⮡ ६   ᪮ ࠭:





short video = __dpmi_segment_to_descriptor(0xa000);

short video = __dpmi_segment_to_descriptor(0xa000);



movedata(_my_ds(), buffer, video, 0, 320*200);





* For the fastest access to memory outside your usual address space, you might consider using the "nearptr" functions declared on the `<sys/nearptr.h>' header; see the library reference for more details. Also see the description of how to get the fastest direct access to peripheral devices in Section 18.6, below.

*  ᠬ ண 㯠    襣 筮 ᭮ ࠭⢠,    ᯮ짮 㭪権 nearptr,   " <sys/nearptr.h> ' ; .  뫪  襣 ⢠ ஡⥩.  . ᠭ ⮣,   ᠬ  אַ   ਩ ன   18.6, .





18.5 Conventional-memory addresses use only 20 bits

18.5   ⠭⭮  ᯮ ⮫쪮 20 

===================================================

===================================================





**Q*: I call `movedata' to pass data between my program and the transfer buffer, but get bogus values or General Protection Fault.*

** Q*:   뢠 "movedata", ⮡ ।    ணࠬ  ஬ ।,    祭   ࠢ 





*A* :  Valid conventional-memory addresses are only 20 bit-wide.  However, the value stored in the variable `_go32_info_block.linear_address_of_transfer_buffer' (or its alias, `__tb') is not guaranteed to have the higher 12 bits zeroed, and `movedata' doesn't mask those high bits, because it can also be used to move data between 2 protected-memory locations.  Be sure to mask off the high 12 bits of the value returned by various `..._linear_address_...' fields in DJGPP structures, whenever that address references a conventional memory location, before you call *any* of the functions from the `movedataX' family, the "farptr" or the "nearptr" functions.

*A*: ⨬    ⠭⭮  - ⮫쪮 20 ࠧ來. , 祭, ࠭  ६ " _go32_info_block.linear_address_of_transfer_buffer ' ( ᥢ, " __ tb ')     ﬨ  12 ,  "movedata"  ᪨  訥 ࠧ, ⮬   㭪  ⠪ ᯮ짮, ⮡ ६   2 ᯮﬨ  頥 . ,  ᪨஢ 訥 ⮢ 12 ⭮ 祭, 饭 ࠧ " . . . _linear_address_ ... ' ﬨ   DJGPP, 直 ࠧ,    뫠  ⠭⭮ ᯮ , ०, 祬  뢠 ** 㭪権  "movedataX" ᥬ⢠(ਨ), "farptr"  㭪権 nearptr.





18.6 Fast access to memory-mapped devices or absolute addresses

18.6  㯮  ன⢠  ⮡ࠦ    ᮫ ᠬ

===============================================================

===============================================================





**Q*: The "farptr" functions are too slow for my application which *MUST* have direct access to a memory-mapped device under DPMI.  How can I have this in DJGPP?  My entire optimized graphics library is at stake if I can't! :(*

** Q*: 㭪樨 farptr ᫨誮    ਫ, ஥ **  אַ   ன⢮  ⮡ࠦ   DPMI.       DJGPP?   ⨬஢ ᪠ ⥪ -  ஧, ᫨     ᤥ!: (*





*A* :  The following so-called Fat DS method was suggested by Junaid A. Walker <junaid@barney.eng.monash.edu.au> (he also posted a program which uses this technique to access the video RAM; you can look it up by searching the mailing list archives).  But first, a word of warning: the method I'm about to describe effectively disables memory protection, and so might do all kinds of damage if used by a program with a wild pointer.  Or, as Stephen Turnbull <turnbull@shako.sk.tsukuba.ac.jp> has put it:

*A*: 騩 ⠪ 뢠  ⮤ DS  । Junaid A.  < junaid barney.eng.monash.edu.au > ( ⠪ ॣ஢ ணࠬ,  ᯮ  ⮤, ⮡    - ;   ᬮ ,   娢 ᯨ᪠ ⮢).  ᭠砫, ।०: ⮤,   ᮡ 뢠 ⢨⥫쭮, ⪫砥  ,  ⠪      ०, ᫨ ᯮ ணࠬ   㪠⥫. , ᪮ ⨢ ୡ㫫 < turnbull shako.sk.tsukuba.ac.jp > ⨫ :





*Surgeon General's WARNING*:  The description below uses the "Fat DS hack", a steroid derivative which gives your program great strength, a thick neck, baldness, and is known to be closely linked with the Alzheimer's disease.

*Surgeon WARNING* ࠫ: ᠭ  ᯮ "  dpkjv DS ", ந ந,   襩 ணࠬ  殮, ⮫ , baldness,   ⭮,  㤥  易   奨.





Having said that, here is the trick: you change the limit of the segment descriptor stored in `DS' to `0xffffffff' (i.e., -1), using function 8 of the DPMI interrupt 31h.  After that, you have access to all the memory which is currently mapped in.  You then use the 32-bit wrap-around in the linear address space to access memory at, say, linear address 0xa0000 (which belongs to the VGA), or any other address on your memory-mapped device.

 ,  ਥ:   ࠭祭 ⥫ ᥣ, ࠭  " DS "  0xffffffff ' ( , -1), ᯮ 㭪 8 뢠 DPMI 31h( । ᥣ). ᫥ ⮣,     ᥩ ,   饥 ६ ⮡ࠦ.  ⥬ ᯮ 32-ࠧ來 横᪨ ७   ᭮ ࠭⢥, ⮡     ᪠,   ᮬ 0xa0000 ( ਭ VGA),   㣮   襬 ன⢥  ⮡ࠦ .





You should know up front that this trick won't work with every DPMI host. Linux's DOSEmu and Win/NT won't allow you to set such a huge limit on the memory segment, because these operating systems take memory protection seriously; in these cases `__djgpp_nearptr_enable' will return zero--a sign of a failure.  CWSDPMI, QDPMI, Win 3.x and Win 9x all allow this technique (OS/2 Warp seems to allow it too, at least as of version 8.200), but some events break this scheme even for those DPMI hosts which will allow it.  A call to `malloc' or any other library function which calls `sbrk' might sometimes change the base address of the `DS' selector and break this method unless the base address is recomputed after `sbrk' call.  (The "nearptr" functions support this recomputation by providing you with the `__djgpp_conventional_base' variable, but it is your responsibility to use it.)  The same change happens when you call `system', and as a result of some other events external to the executing code thread, like multitasking or debugger execution.

  ,   ਥ  㤥 ࠡ   DPMI ஬.  DOS Linux  Win/NT  㤥   ⠭ ⠪ ஬ ࠭祭 ᥣ , ⮬   樮 ⥬    쥧;    " __ djgpp_nearptr_enable '   -  ⪠. CWSDPMI, QDPMI, Win 3 x  Win 9x    ⮤ (OS/2   ,  ⠪,  ࠩ   8.200),   ᮡ ࠧ  奬    DPMI ஢,   . 饭  "malloc"   㣮 筮 㭪樨,  뢠 "sbrk",      " ᥫ DS  ࠧ  ⮤, ᫨    ୮  ᫥ ᫥ " sbrk ' 饭 (. " Nearptr " 㭪樨 ন  ॢ᫥ ⥬, ᫨   " __ djgpp_conventional_base ' ६,    襩 ⢥⢥, ⮡ ᯮ짮 .)   ᠬ  砥,   뢠 "⥬",   १  㣨 ᮡ⨩, 譨  믮饩  ,  믮 ⫠稪  筮 ०.





You should also know that the `__djgpp_nearptr_enable' function in DJGPP v2.0 didn't verify that the limit was properly set.  So if the DPMI server would fail the call *silently*, the function won't detect it and will not return a failure indication.  DJGPP v2.01 corrects this omission by always verifying that the DPMI host has honored the request, and returns a failure indication if it hasn't.

  ⠪ ,  " __ djgpp_nearptr_enable ' 㭪  DJGPP v2. 0  ஢, 뫮  ࠢ쭮 ⠭ ࠭祭.  DPMI ࢥ ௨  * * 㤠    饭, 㭪  㤥 㦨    㤥   ⪠. DJGPP v2. 01 ࠢ  ᪫祭,  ᥣ ஢,  DPMI  㤮⢮ਫ ,  頥  ⪠, ᫨  ⠪.





If you are aware of these limitations, and don't need your code to run under all DPMI hosts, it might be the fix to your problems.

᫨    ࠭祭,   㦤  襬 , ⮡ 믮  ࠢ  DPMI ஢,   䨪஢  ஡.





Confused about how exactly should you go about using this technique in your program?  Look at the docs of the "nearptr" functions, See __djgpp_nearptr_enable in "libc.a reference", or point your Web browser to http://www.delorie.com/djgpp/doc/libc-2.01/libc_122.html#SEC122.

⠭ ⭮⥫쭮 ⮣,  筮    ⭮⥫쭮 ᯮ짮 ⮩ ⮤  襩 ணࠬ? ᬮ 㬥 㭪権 nearptr, . __ djgpp_nearptr_enable  " libc.a 뫪 ",  㪠   ᬮ   http: // www.delorie.com/djgpp/doc/libc-2.01/lib c_122. html * SEC122.





Another possibility is to use the DPMI function `0x508' that can map any range of physical memory addresses into a block that you allocate.  Note that this is a DPMI 1.0 functionality which is *not* supported by most DPMI 0.9 hosts (`CWSDPMI' does support it).  There is a helper function `__djgpp_map_physical_memory' in the DJGPP C library that you can use to call these services.

㣠  ⮨  ⮬, ⮡ ᯮ짮 㭪 DPMI "0x508",   ⮡ࠦ   䨧᪨ ᮢ   ,   ।.  ,   㭪 DPMI 1.0,   ** ন묨 設⢮ DPMI 0.9 ஢ ("CWSDPMI" ন ).  㭪 魨 " __ djgpp_map_physical_memory '  DJGPP ⥪ C,    ᯮ짮, ⮡ 맢  㣨.





18.7 Accessing absolute address above 1MB

18.7 饭  ᮫ ᠬ  1

=========================================

=========================================





**Q*: How can I access memory-mapped peripheral devices (or any other absolute address) above 1 MByte mark?*

** Q*:      ਩ ன⢠  ⮡ࠦ  (  㣮 ᮫ )  祬 ⪠ 1 ? *





*A* :  You should use DPMI functions to allocate an LDT descriptor, and map it to an absolute physical address.  You can then use the functions from <sys/farptr.h> to access that linear address.  These are the DPMI calls that you will have to use:

*A*:   ᯮ짮 㭪樨 DPMI, ⮡ । LDT ਯ,  ⮡ࠦ   ᮫ 䨧᪨ ᠬ.   ⥬ ᯮ짮 㭪樨  <sys/farptr.h>, ⮡   ⮬  .  맮 DPMI,   㤥  ᯮ짮:





- allocate an LDT descriptor (Int 31h/AX=0);

- । LDT ਯ (Int  31h/AX = 0);





- map selector to physical address (Int 31h/AX=0800h);

- ⮡ࠧ ᥫ  䨧᪮  (Int 31h/AX = 0800);





- lock linear address (Int 31h/AX=0600h);

- ஢   (Int 31h/AX = 0600);





- set segment base address (Int 31h/AX=7);

- ⠭  砫 ᬥ饭 (Int 31h/AX = 7);





- set segment limit (Int 31h/AX=8).

- ⠭ ࠭祭 ᥣ (Int  31h/AX = 8).





All of these DPMI calls have `__dpmi__XXX' wrappers in the DJGPP library.

  饭 DPMI  " __ dpmi __ XXX ' 窨  ⥪ DJGPP.





18.8 How to make DOS/BIOS call your function

18.8   ⮡ DOS / BIOS 뢠  㭪

============================================

============================================





**Q*: How can I make any real-mode service call my function?  E.g., the mouse driver has a provision (function 0Ch) to call a user-defined handler when certain events occur, which expects a far pointer to my function in the `ES:DX' register pair.*

** Q*:      㦨 ॠ쭮 ०,  뢠  㭪? ਬ, ࠩ   ।⢮ (㭪 0Ch) ⮡ 맢 ।塞 짮⥫ ࠡ稪,   ᮡ ந室, ஬ 室  㪠⥫   㭪   ॣ "ES:DX"





*A* :  Those services expect a real-mode function, so you should wrap your protected-mode function with a real-mode stub.  To this end, call either the `_go32_dpmi_allocate_real_mode_callback_retf' or the `_go32_dpmi_allocate_real_mode_callback_iret' library function, as required by the real-mode service you want to hook, and pass the `segment' and `offset' fields it returns to the service you want (in the above example, Int 33h function 0Ch) by calling `__dpmi_int.' Here's a code fragment that shows how to do this:

*A*:  㣠 室 㭪 ॠ쭮 ०, ⠪      襩 㭪樨  饭 ०   誨 ॠ쭮 ०.  , 맮  " _go32_dpmi_allocate_real_mode_callback_ retf '  " _go32_dpmi_allocate_real_mode_callback_ iret '  㭪,  ॡ ࢨᮬ ॠ쭮 ०,    楯,  ।  "ᥣ"  "ᬥ饭",    ⮬ ࢨ ( 㯮⮬ ਬ, Int 33 㭪 0Ch) 뢠 " __ dpmi_int. '  ࠣ ,  뢠,   :









#include <dpmi.h>

#include <dpmi.h>

#include <go32.h>

#include <go32.h>





static __dpmi_regs        callback_regs; static _go32_dpmi_seginfo callback_info;

static __dpmi_regs        callback_regs; static _go32_dpmi_seginfo callback_info;





int install_mouse_handler (unsigned mask, void (*func)(__dpmi_regs *)) {

int install_mouse_handler (unsigned mask, void (*func)(__dpmi_regs *)) {

__dpmi_regs r;

__dpmi_regs r;





callback_info.pm_offset = (long)func; if (_go32_dpmi_allocate_real_mode_callback_retf(&callback_info, &callback_regs)) return -1;  /* failure */

callback_info.pm_offset = (long)func; if (_go32_dpmi_allocate_real_mode_callback_retf(&callback_info, &callback_regs)) return -1;  /* failure */





r.x.ax = 0xc; r.x.cx = mask; __dpmi_int (0x33, &r); return (r.x.flags & 1) ? -1 : 0;

r.x.ax = 0xc; r.x.cx = mask; __dpmi_int (0x33, &r); return (r.x.flags & 1) ? -1 : 0;

}

}





The handler (`func' in the above example) will be called with a pointer to a `__dpmi_regs' structure which is filled by values found in the CPU registers when the mouse driver calls the handler.  See the docs in the library reference Info file for further details about allocating wrapper functions.

ࠡ稪 ("func"  㯮⮬ ਬ) 㤥 뢠  㪠⥫   `__ dpmi_regs',   祭ﬨ, 묨  ॣ  ,  ࠩ  뢠 ࠡ稪. . 㬥  筮 뫪 䠩 ଠ樨  쭥 ஡⥩ ⭮⥫쭮 । 㭪権 窨.





18.9 How to hook hardware interrupts

18.9  墠뢠  뢠

====================================

====================================





**Q*: How do I register my DJGPP function as a hardware interrupt handler?*

** Q*:   ॣ஢  㭪 DJGPP  ࠡ稪 ⭮ 뢠? *





*A* :  The optimal setup depends on the interrupt frequency and on the amount of processing it requires.  Therefore, only some basic considerations and techniques are listed below.  What combination of these is best for your application is up to you to decide.

*A*: ⨬쭠 ⠭    뢠  ⢠ ࠡ⪨ ஥ ॡ. ⥫쭮, ⮫쪮   ᬮ७  ⮤ ᫥ .     ᠬ 襩  襣 ਫ -   ᠬ.





First, some background.  Hardware interrupts can occur when the processor is either in real mode (like when your program calls some DOS service) or in protected mode.  When your program runs under a DPMI host, hardware interrupts are caught by the DPMI host and passed to protected mode first; only if unhandled, they are then reflected to real mode.  Therefore, in DPMI mode you can get away with installing only a protected-mode handler. However, if the interrupts happen at a high frequency (say, more than 10 KHz), then the overhead of the interrupt reflection from real to protected mode might be too painful, and you should consider installing a real-mode interrupt handler in addition to the protected-mode one.  Such a real-mode handler will be called *before* the interrupt gets to the DPMI host, and handle the interrupt entirely in real mode, so it must be written in assembly and located in conventional memory (below the 1MB mark).  If you need to hook an interrupt with both PM and RM handlers, you must hook the PM interrupt first, then the RM one (because hooking the PM interrupt modifies the RM one).  Also, you should know that some DPMI hosts don't allow you to hook the RM interrupt (CWSDPMI does); the only way to be sure is to try.

砫,  ⮢.  뢠  ந室,   室  ॠ쭮 ० ( ⮬,   ணࠬ 뢠 ஥ 㦨 DOS)   饭 ०.   ணࠬ 믮  DPMI ஬,  뢠 墠祭 DPMI ஬  ।  饭 ० ᭠砫; ⮫쪮 ᫨  ࠡ⠭,  ⥬ ࠦ  ॠ ०. ⥫쭮,  DPMI ०   ⠭ ⮫쪮 ࠡ稪  饭 ०. , ᫨ 뢠   ᮪  (,  祬 10 ),  ந⥫  ࠦ ᨣ 뢠  ॠ쭮  饭 ०    ᫨誮 ,    ᬮ ⠭ ணࠬ ࠡ⪨ 뢠 ॠ쭮 ०     饭 ०.  ࠡ稪 ॠ쭮 ० 㤥 뢠 *।*  뢠 DPMI ,  ਯ 뢠   ॠ쭮 ०, ⠪     ᠭ  ᥬ  ࠧ饭  ⠭⭮  ( ⪨ 1). ᫨   墠 뢠 PM  RM ࠡ稪,   墠 PM 뢠 ᭠砫, ⥬ RM  (⮬  墠 PM 뢠  RM ). ,   ,   DPMI     墠뢠 뢠 RM (CWSDPMI  ); ⢥ ᯮᮡ 㡥 ⮨  ⮬, ⠪  ஡.





To install a protected-mode interrupt handler, you do this:

⮡ ⠭ ணࠬ ࠡ⪨ 뢠  饭 ०,   ᤥ:





* In general, your handler should be written in assembly to be bullet-proof.  It should lock all the memory (code, data and stack) it touches during interrupt processing (this is virtually impossible in C), explicitly issue the `STI' instruction before `IRET' and perform all the other chores described in the DPMI spec (see DOS Protected Mode Interface Specification in Section 22.4).  To install assembly handler, you should do this:

*  饬,  ࠡ稪   ᠭ  ᥬ, ⮡  㫥஡.   ஢   (,   ⥪) 祣  ᠥ  祭 ࠡ⪨ 뢠 ( 㠫쭮   C),  뤠 "STI"  ० "IRET"  믮  㣨 ࠡ, ᠭ  DPMI ᯥ䨪 (. 䨪 䥩 饭 ० DOS   22.4). ⮡ ⠭ ᠬ ࠡ稪,    :





- Call `__dpmi_get_protected_mode_interrupt_vector' and save the structure it returns (to restore the previous handler address before your program exits).

- 맢 " __ dpmi_get_protected_mode_interrupt_vector ',  ࠭    (⮡ ⠭ ।騩  ࠡ稪 । 訬 室  ணࠬ).





- Lock all the memory your handler touches with a series of calls to `__dpmi_lock_linear_region.'

- ஢   ன ᠥ  ࠡ稪  ਨ 饭   " __ dpmi_lock_linear_region. '





- Finally, call `__dpmi_set_protected_mode_interrupt_vector' passing it the pointer to a `__dpmi_paddr' structure filled with `_my_cs' in the `selector' field and the address of your function in the `offset32' field.

-  祭, 맮 " __ dpmi_set_protected_mode_interrupt_vector '  ⮬ । 㪠⥫  " __ dpmi_paddr ' ,  " _my_cs'   " ᥫ '   襩 㭪樨  " offset32 ' .





* If your handler function is written in C, you should generally call the `_go32_dpmi_XXX' functions instead of the bare-bones API wrappers whose names start with `__dpmi_.'  Specifically:

* ᫨  㭪 ࠡ稪 ᠭ  C,    맢 㭪樨 _go32_dpmi_XXX  㭪権 稭  " __ dpmi_. ' 樠쭮:





- Call `_go32_dpmi_get_protected_mode_interrupt_vector.'  This function puts the selector and offset of the specified interrupt vector into the `pm_selector' and `pm_offset' fields of the structure pointed to by its second argument.  This data should be saved and later passed to `_go32_dpmi_get_protected_mode_interrupt_vector' to restore the vector on exit.

- 맢 " _go32_dpmi_get_protected_mode_interrupt _vector. '  㭪 頥 ᥫ  ᬥ饭 ।  뢠  "pm_selector"  "pm_offset"  , 㪠  ஬ ࠬ.     ࠭   ।  " _go32_dpmi_get_protected_mode_interrupt _vector ', ⮡ ⠭   室.





- Call `_go32_dpmi_allocate_iret_wrapper,' passing it the address of your functions in the `pm_offset' field and the value of `_my_cs' in the `pm_selector' field.  The `pm_offset' field will get replaced with the address of the wrapper function which is a small assembler function that handles everything an interrupt handler should do on entry and before exit (and what the code GCC generates for an ordinary C function doesn't include); the effect is similar to using interrupt or `_interrupt' keyword in some DOS-based compilers.

-  "_go32_dpmi_allocate_iret_wrapper", ।    㭪権  "pm_offset"   祭 " _my_cs'  " pm_selector ' . " Pm_offset '  ⠭    㭪樨 窨,   쪮 㭪樥 ᥬ,  ࠡ뢠 ,  ணࠬ ࠡ⪨ 뢠    室  । 室 (   GCC   筮 㭪樨 C,  砥); 䥪  ᯮ짮 뢠  " _interrupt ' 祢 ᫮   -᭮ .





- If you want your handler to chain to the previous handler, call `_go32_dpmi_chain_protected_mode_interrupt_vector.'  This will set up a wrapper function which, when called, will call your handler, then jump to the previous handler after your handler returns.  Put the address of your handler into the `pm_offset' field and the value of `_my_cs' into the `pm_selector' field of the `_go32_dpmi_seginfo' structure and pass a pointer to it to this function.

- ᫨   ⮡  ࠡ稪 ⤠ ࠢ  楯窥  ।饬 ࠡ稪, 맮 " _go32_dpmi_chain_protected_mode_interrupt_vector. '  ⠭ 㭪 窨 ,   맮, 맮  ࠡ稪, ⥬ ३  ।饬 ࠡ稪 ᫥ 襣   ࠡ稪.   襣 ࠡ稪  "pm_offset" ,  祭 " _my_cs'  " pm_selector '  " _go32_dpmi_seginfo '   । 㪠⥫   㭪.





- You then call `_go32_dpmi_set_protected_mode_interrupt_vector' with the address of the `_go32_dpmi_seginfo' structure you got either from `_go32_dpmi_allocate_iret_wrapper' or from `_go32_dpmi_chain_protected_mode_interrupt_vector.'

-  ⥬ 뢠 " _go32_dpmi_set_protected_mode_interrupt _vector '  ᮬ  _go32_dpmi_seginfo,   稫   "_go32_dpmi_allocate_iret_wrapper"   " _go32_dpmi_chain_protected_mode_interrupt_vector. '





The problem with writing handlers in C as above is that the wrappers' code and data aren't locked, and in practice you can't lock all of memory the handler itself uses, either.  Thus, this approach is generally unsuitable for production-quality software and should be used only when the program is known not to page (i.e., only the physical memory is used).  You might consider disabling virtual memory to make sure your program doesn't page.  To accomplish this, either set the `_CRT0_FLAG_LOCK_MEMORY' bit in the `_crt0_startup_flags' variable, or use `CWSDPR0' or `PMODE/DJ' as your DPMI host.  In fact, using one of these methods is the recommended way of debugging the first versions of a program that hooks hardware interrupts; only after you are sure that your basic machinery works should you move to testing it in a setup when paging might happen.

஡  ᠭ ࠡ稪  C   ⮨  ⮬,   祪    ஢,  ࠪ᪨    ஢  ,   ࠡ稪 ।⢥ ᯮ, ⠪.  ࠧ,  室  室騩  ஬諥 - ⢥ ணࠬ ᯥ祭   ᯮ짮 ⮫쪮,  ணࠬ ⭠  ࠭ ( , ⮫쪮 䨧᪠  ᯮ).    ,  ⪫祭 㠫쭮  ࠭,   ணࠬ   ࠭. ⮡ 믮 , ⠭ "_CRT0_FLAG_LOCK_MEMORY"   " _crt0_startup_flags' ६,  ᯮ " CWSDPR0 '' PMODE/DJ '   DPMI . ᪨, ᯮ짮    ⮤ - ४㥬 ᯮᮡ ⫠  ᨩ ணࠬ,  墠뢠  뢠; ⮫쪮 ᫥ ⮣,   㢥७,   ᭮ 㤮 ࠡ⠥,     ஢ ⮣  ⠭,  ⠭   .





Note that `_CRT0_FLAG_LOCK_MEMORY' is only recommended for small programs that run on a machine where enough physical memory is always available, because the startup code currently doesn't test if memory is indeed locked, and you can end up with unlocked, or partially unlocked memory, which will crash your program.

 ,  "_CRT0_FLAG_LOCK_MEMORY" ⮫쪮 ४   ணࠬ,  믮﫮  設,  筮 䨧᪮ ⨨  ᥣ 㯭, ⮬   ᪠  饥 ६  ஢, ᫨   ⢨⥫쭮 ஢,    稢  ࠧ஢,  筮 ࠧ஢ ,  ࠧ  ணࠬ.





To install a real-mode interrupt handler, you do this:

⮡ ⠭ ணࠬ ࠡ⪨ 뢠 ॠ쭮 ०,   :





* Call `__dpmi_get_real_mode_interrupt_vector' and save the structure it returns (to restore the previous handler address before your program exits).

* 맢 " __ dpmi_get_real_mode_interrupt_vector ',  ࠭    頥 (⮡ ⠭ ।騩  ࠡ稪 । 訬 室  ணࠬ).





* Allocate some conventional memory with `__dpmi_allocate_dos_memory' and put the code of your handler there with the `dosmemput' function.  (You could also call one of the functions which allocate a real-mode call-back, but these will cause a mode switch on every interrupt, which you want to avoid; otherwise there is no point in installing a real-mode handler, right?)

* ।  ⠭   " __ dpmi_allocate_dos_memory ',    襣 ࠡ稪 ⠬   㭪樨 dosmemput. (   ⠪ 뢠   㭪権,  । 楤   ॠ ०,    뢠 ०,   뢠, ண   ;   ᫠   ⠭ ࠡ稪 ॠ쭮 ०, ࠢ쭮?)





* Put the address which `__dpmi_allocate_dos_memory' returned into a `__dpmi_raddr' structure (the lower 4 bits into `offset16' field, the rest into `segment' field), then call `__dpmi_set_real_mode_interrupt_vector.'

*    " __ dpmi_allocate_dos_memory ' 饭  " __ dpmi_raddr '  ( 訥 4   "offset16" , ⮪   "ᥣ"), ⥬ 맮 " __ dpmi_set_real_mode_interrupt_vector. '





For examples of installing and using hardware interrupt handlers, see the sample code written by Bill Currie <bill_currie@MAIL.TAIT.CO.NZ>, the Sound Blaster interrupt-driven functions, the `mkkbd' package, and the `libhw' library, described under sample DJGPP packages in Section 22.2.  Alaric B. Williams <alaric@abwillms.demon.co.uk> has written a tutorial on DJGPP interrupt handling, at this URL:

 ਬ஢ ⠭  ᯮ짮 ࠡ稪 ⭮ 뢠, . ⨯ , ᠭ  ᮮ⢥⢨  ⮬ Currie < bill_currie MAIL.TAIT.CO.NZ >, Sound Blaster 뢠 㭪樨, "mkkbd" ,  "libhw" ⥪, ᠭ  ⨯묨 ⠬ DJGPP   22.2. Alaric B. Williams < alaric abwillms.demon.co.uk > ᠫ  ணࠬ  ࠡ⪥ 뢠 DJGPP,  ⮬ URL:





http://www.abwillms.demon.co.uk/prog/djints.txt

http://www.abwillms.demon.co.uk/prog/djints.txt





18.10 Should I use _go32_XXX or __dpmi_YYY functions?

18.10    ᯮ짮 㭪樨 _go32_XXX  __ dpmi_YYY?

=====================================================

=====================================================





**Q*: In v1.x I was used to the `_go32_...' functions, but now comes v2 which also has `__dpmi_...' functions.  Are there any differences between these two varieties?*

** Q*:  v1. x  ᯮ짮 " _go32_ ... ' 㭪樨,  ⥯ 宦 v2,  ⠪  " __ dpmi_ ... ' 㭪樨.    ࠧ  ⨬  ⢠? *





**Q*: Do I need to convert my old v1.x code to use the new `__dpmi_...' functions?*

** Q*:   ८ࠧ   v1. x , ⮡ ᯮ짮  " __ dpmi_ ... ' 㭪樨? *





*A* :  These two groups of functions have different functionality, so don't just substitute the new ones for the older ones, because it usually won't work!  The new `__dpmi_...' functions are just bare-bones wrappers of the DPMI API calls (see DPMI Specification in Section 22.4), generally unsuitable for use with handlers written in C, whereas the old `_go32_...' functions are intelligent helper functions which only make sense if your interrupt handlers are C functions.  The problem with the `_go32_...' functions is that they don't lock all the code and data they (and your handlers) use, so they can crash on memory-tight machines and thus aren't suitable for production-quality code.  But they are certainly useful in the initial stages of writing and debugging code that hooks hardware interrupts, and for migrating existing v1.x code to v2.  Some of the old names were just `#define'd' to the new names where the functionality is identical.

*A*:   㯯 㭪権  ࠧ 㭪樮 , ⮬    묨  筮  㤥 ࠡ!  " __ dpmi_ ... ' 㭪樨 - ⮫쪮 窨  DPMI 饭 API (. DPMI 䨪   22.4),  室騩  ᯮ짮  ࠡ稪, ᠭ묨  C,   ६   " _go32_ ... ' 㭪樨 - ⥫㠫 㭪樨 魨,  ⮫쪮   ᫨,  ணࠬ ࠡ⪨ 뢠 - 㭪樨 C. ஡  " _go32_ ... ' 㭪樨 ⮨  ⮬,          (  ࠡ稪) ᯮ, ⠪    ࠧ  墠⪨   ⠪ ࠧ  室  ஬諥 - ⢥ .   筮   砫 ⠤   ⫠ ,  墠뢠  뢠,   ६饭 饣 v1. x   v2.     뫨 ⮫쪮 " * define'd '   ,  㭪樮  .





The bottom line is that it shouldn't be necessary to convert your code for it to work at least as well as it did in v1.x; but if you want it to be more stable, you should rewrite your handlers in assembly and use the new `__dpmi_...' functions (see How to install a hardware interrupt handler in Section 18.9).

 ப- ,     室 ८ࠧ    ⮣, ⮡ ࠡ,  ࠩ  ⠪     v1. x;  ᫨  , ⮡  뫮  ⮩稢,   १  ࠡ稪   ᥬ  ᯮ짮  " __ dpmi_ ... ' 㭪樨 (.,  ⠭ ࠡ稪 ⭮ 뢠   18.9).





18.11 Hardware interrupt hooking has its subtleties ...

18.11 墠 ⭮ 뢠  ⮭ ...

=======================================================

=======================================================





**Q*: I did all the above, but my program occasionally still hangs...*

** Q*:    ,   ணࠬ    ਢ  ᠭ ...*





*A* :  Hooking hardware interrupts in DJGPP (and in protected mode in general) has a few subtle aspects.  In general, hardware interrupt handling in DJGPP v2.x is rock solid *if you play by the rules*.  Unfortunately, the rules are a bit tricky.

*A*: 墠  뢠  DJGPP (  饭 ० )  ᪮쪮 ⮭ ᯥ⮢. , ࠡ⪠ ⭮ 뢠  DJGPP v2. x - 㤭 * ᫨  ࠥ  ࠢ*.  ᮦ, ࠢ  ᫮.





One cause of your problems might be that your interrupt handler or some memory location it uses get paged out because of the virtual memory mechanism, or because your program spawned a child program.  In that case, the interrupt might cause a call to a non-existent service routine, with the obvious results.  You should lock all the memory pages that your handler accesses by calling the `__dpmi_lock_linear_region' library function.  This also means in practice that you should write your handler in assembly, as described in how to set an interrupt handler in Section 18.9, above.  You can disable virtual memory, or put `_CRT0_FLAG_LOCK_MEMORY' into `_crt0_startup_flags' to make sure nothing is paged out (but then your program might not have enough memory to run, unless you run on memory-abundant systems).

 稭  ஡    ,   ணࠬ ࠡ⪨ 뢠    ,  ࠡ稪 ᯮ  ࠭筮 ᢮ - 㠫쭮 堭 ,  ⮬   ணࠬ த  ணࠬ.  ⮬ 砥, 뢠   뢠 饭  饩 ࢨ᭮ ணࠬ,  祢묨 १⠬.   ஢  ࠭   ᯮ  ࠡ稪,  뢠 " __ dpmi_lock_linear_region ' 筠 㭪.  ⠪ 砥 ࠪ᪨,      ࠡ稪  ᥬ,  ᠭ  ࠧ 18.9, .   ⪫ 㠫 ,   "_CRT0_FLAG_LOCK_MEMORY"  " _crt0_startup_flags', ⮡ 㤮⮢,    㤥  ࠭ ᢮ ( ⥬  ணࠬ  ᬮ  筮 , ⮡ 믮, ᫨   믮  ⥬  墠⠥ ).-   ஢ 墠   .





Another problem might be that the hardware peripheral you use generates a lot of interrupts.  Due to specifics of hardware interrupts handling in protected mode, there is a substantial overhead involved with reflection of interrupts between real and protected modes.  For instance, on a 486DX/33 this reflection might consume up to 3000 clocks; on a 386SX/16, even a 1KHz clock might eat up 1/2 of available cycles.  If your hardware fires too many interrupts, your CPU might not be able to keep up.  In that case, consider reducing the interrupt frequency, or move some of the processing done inside the interrupt handler to some other place.  Use a ring 0 DPMI server such as `CWSDPR0' or `PMODE/DJ' which don't swap interrupt stacks--this will reduce the overhead of the interrupt reflection to some degree.  If your handler is written in C, write it in assembly and make sure it doesn't chain.  If that doesn't help, install a real-mode handler.

㣠 ஡     ⮬,   ன⢠,   ᯮ,   뢠. - ᯥ樠 ᮮ饭 ࠡ⪨  뢠  饭 ०,  ॠ ந⥫ ,  ࠦ ᨣ 뢠  ॠ묨  饭묨 ०. ਬ,  486DX/33  ࠦ ᨣ   ॡ  3000 横 ;  386SX/16,  1KHz    쥤 1/2 㯭 横. ᫨    ஦ ᫨誮  뢠,  CPU  ᬮ   ᯮᮡ ঠ   宧⢮  ᮪ ஢.  ⮬ 砥, ᬮ 㬥襭  뢠,  ६  ࠡ⪨  㣮 . ᯮ  0 DPMI ࢥ ⨯ "CWSDPR0"  "PMODE/DJ",    ⥪ 뢠 -  㬥 ந⥫  ࠦ ᨣ 뢠  ன ⥯. ᫨  ࠡ稪 ᠭ  C,    ᥬ,  㤮⮢,     楯. ᫨   , ⠭ ࠡ稪 ॠ쭮 ०.





Some losing memory managers, notably EMM386, were reported to induce a high interrupt handling overhead.  In one case, a user reported an increase in the interrupt rate from 2 KHz to 6 KHz after uninstalling EMM386.

 娥 ᯥ , ᮡ EMM386,  ᮮ頫,  ⨬㫨஢ ᮪ 뢠, ࠡ뢠饥 ந⥫ .   砥, 짮⥫ ᮮ騫  㢥祭 ᪮ 뢠  2   6  ᫥ ⠭ EMM386.





Still another possibility is that you use a non-default `sbrk' algorithm in your program (check if the header file `crt0.h' is included anywhere in the program, and if so, if the `_CRT0_FLAG_UNIX_SBRK' bit in the `_crt0_startup_flags' variable is set by the program.  If it is, then a hardware interrupt which happens at the wrong time could crash your machine, especially if you run under Windows 3.x.

  㣠  ⮨  ⮬,   ᯮ "sbrk"    㬮砭  襩 ணࠬ (஢ઠ, ᫨ 䠩  " crt0. h ' 祭 -  ணࠬ,  ᫨ ⠪, ᫨ "_CRT0_FLAG_UNIX_SBRK"   " _crt0_startup_flags' ६ ⠭ ணࠬ. ᫨  ⠪,  ⭮ 뢠, ஥ 砥  ࠢ쭮 ६,   ࠧ  設, ᮡ, ᫨  믮  ࠢ Windows 3x.





You should also keep in mind that the DPMI server can decide to handle some of the interrupts itself and not pass them to your program, although this is rare.  For example, Win95 won't pass the Ctrl-Alt-Del combination to your keyboard interrupt handler, but will rather act on it itself; QDPMI sometimes processes Ctrl-C presses so that your program never sees them, etc. Sometimes, but not always, you can change some configuration option to make some keys get to your handler (e.g., the Alt-TAB setting on the Win3.x `.PIF' file).

  ⠪   ,  DPMI ࢥ   ࠡ뢠   뢠 ᠬ⥫쭮   ।  襩 ணࠬ,   ।. ਬ, Win95  㤥 । Ctrl-Alt-Del  襩 ணࠬ ࠡ⪨ 뢠 ,  㤥 ⢮ ᠬ; QDPMI  ࠡ뢠 ᠬ⥫쭮 Ctrl-C  .. ,   ᥣ,   ,  樨 䨣樨, ⮡    㯭  襣 ࠡ稪 (ਬ, ⠭ Alt-TAB   Win3. x ". PIF ' 䠩).





If the above still doesn't explain your problem, then post your code on comp.os.msdos.djgpp news group or the djgpp mailing list <djgpp@delorie.com>, tell there how it fails and somebody will usually have a solution or a work-around for you.

᫨ 㯮      ஡,  ࠢ      comp.os.msdos.djgpp 㯯 ⥩  djgpp ᯨ᪥ ⮢ < djgpp delorie.com >, ᮮ頥 ⠬,   ௨ 㤠,   -  㤥 筮  襭  ࠡ  .





18.12 How to read and write ports

18.12    뢠 

=================================

=================================





**Q*: I need to read from and write to PC ports, and I'm accustomed to using the `inp' and `outp' functions.  But I hear they aren't available in DJGPP?*

** Q*:        PC,   祭  ᯮ짮 㭪権 inp and outp.   ,    㯭  DJGPP? *





*A* :  They are in v2.x.  Just  `#include <pc.h>'  and you get their prototypes.  The functions themselves are in the default library.  Note that there are also size-specific versions for byte- word- and dword-long access (e.g., `inportl' for reading a 32-bit dword), as well as functions to read/write sequences of bytes and words, like `inportsb' and `outportsw'; these are DJGPP-specific.

*A*:  室  v2. x. 쪮   <pc.h> ',   砥  ⨯. 㭪樨 ।⢥ 室    㬮砭 ⥪.  ,   ⠪ ࠧ-। ᨨ  , ᫮,   ᫮  롮મ (ਬ, "inportl"  ⥭ 32-ࠧ來 dword), ⠪  㭪権  ⥭ -  ਨ ⮢  ᫮,  "inportsb"  "outportsw";  - -ᯥ  DJGPP.





18.13 Inline Assembly code with GCC

18.13  ஥ ᥬ୮   GCC

===================================

===================================





**Q*: I am used to writing inline assembly with Borland C, but can't figure out the way to do it with GCC...*

** Q*:  ᯮ   ஥ ᥬ୮   Borland ,     ᯮᮡ    GCC ...*





**Q*: How can I reference C variables from my inline assembly code?*

** Q*:    뫠  ६ C    ஥ ᥬ୮ ? *





*A* :  GCC has extensive inline assembly facilities.  They allow you to specify everything other compilers let you (like the registers where GCC will put specific results), but in a way that doesn't disable compiler optimizations of the C code that includes inline assembly.  Because of this flexibility, the syntax of the inline assembly code is very different from the other DOS-based compilers.  The GCC on-line docs describe these facilities in detail; to read the relevant sections, type this from the DOS prompt:

*A*: GCC  ᫥ ।⢠ ஥ ᥬ୮ .    । ,  㣨  ᪠   ( ॣࠬ,  GCC  ᯥ᪨ १),   ᯮᮡ,   ⪫砥 ⨬   C,  砥 ஥ ᥬ . - ⮩ , ᨭ⠪  ஥ ᥬ୮  祭 ⫨砥  㣨 -᭮ ஢. GCC ࠪ⨢ 㬥 뢠  ।⢠ ஡; ⮡  室 ࠧ, ⠩   ᪠ DOS:





info gcc "C Extensions" "Extended Asm"

info gcc "C Extensions" "Extended Asm"







(    窨:  .)  㤥, 筮, 㦤  ⮬ ⮭ ⥫ ଠ樨  ⠭묨  襩 ⥬  㯮⮩ , ⮡ ࠡ. ᫨  㦥  ⠭,  䠩 " txi390b. ⮢  '  । DJGPP  ⠭ .





If you read this FAQ via WWW, you can also read about the GCC inline assembly extensions with your Web browser, at this URL:

᫨  ⠥  FAQ १ WWW,   ⠪  ⭮⥫쭮 GCC ७ ஥ ᥬ୮   訬  ᬮ ,  ⮬ URL:





http://www.delorie.com/gnu/docs/gcc/gcc_86.html#SEC89

http://www.delorie.com/gnu/docs/gcc/gcc_86.html#SEC89





