home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / pascal / library / dos / ezdpmi / ezdpmi.pas
Encoding:
Pascal/Delphi Source File  |  1993-08-10  |  21.7 KB  |  638 lines

  1. {==EZDPMI=============================================================
  2.  
  3. A protected mode DPMI access unit. Provides an easy-to-use interface
  4. to the common application-oriented requirements for DPMI: the ability
  5. to interface real-mode drivers and TSRs from protected mode using DOS
  6. memory.
  7.  
  8. EZDPMI is Copyright (c) 1993 by  Julian M. Bucknall
  9.  
  10. VERSION HISTORY
  11. 10Aug93 JMB 1.01 fixed GetMappedDPMIPtr
  12.                  (thanx to Garr Updegraff for spotting this bug)
  13. 21Mar93 JMB 1.00 initial release
  14. ======================================================================}
  15.  
  16. {=====================================================================
  17.  
  18. From numerous threads in the BPASCAL forum of CompuServe, I felt that
  19. there was a real need for a simple unit to access the main features of
  20. DPMI that would be required for a middling to difficult application
  21. program. Having played around with DPMI in both Windows and now in the
  22. new protected mode Borland Pascal, I had a bunch of routines under my
  23. belt that I'd used in real programs, and even written about in the
  24. British programming magazine .EXE in November 1992.
  25.  
  26. My chief consideration in writing these routines was to make things
  27. simple for myself: for example I didn't want to use the DPMI register
  28. structure, I wanted to use the more familiar Registers type (or
  29. TRegisters if you use the WinDOS unit); I wanted to mimic the calling
  30. conventions of the units supplied with Borland Pascal; I wanted an
  31. easy life!
  32.  
  33. The world of protected mode programming brings many benefits (mainly
  34. the very large heap!), but makes other activities much harder (making
  35. sure you have valid pointers is a good one - the number of times
  36. Run-Time error 216 occurs these days!). One of the main problems with
  37. writing programs in protected mode (Windows or no) is interfacing with
  38. real mode DOS, real mode BIOS and real mode DOS drivers/TSRs. The DPMI
  39. manager translates various calls to these interrupts, and others it
  40. leaves well alone (otherwise it would be at least as large as all of
  41. them put together).
  42.  
  43. As an aside, when you call some real-mode interrupt via the Intr
  44. procedure various things happen. Firstly you perform a protected mode
  45. interrupt, not a real mode interrupt (the DPMI manager keeps two
  46. tables of interrupt tables, one for real mode and one for protected
  47. mode). If the interrupt gets handled by a protected mode process that
  48. is not the DPMI manager itself then the latter does nothing - the
  49. protected mode process does it all. If the interrupt points into the
  50. DPMI manager itself then it must make a decision: can it handle the
  51. interrupt, or must it pass the interrupt one into real mode? For the
  52. majority of DOS functions for example, the DPMI manager will handle
  53. the interrupt itself (obviously as a program will make many of them
  54. and it would be more efficient that way). If the DPMI manager doesn't
  55. know about the interrupt, it switches the machine into real mode,
  56. calls the real mode interrupt handler, and on return, switches back
  57. into protected mode and returns to your program. There is one other
  58. action that occurs, and that is the DPMI manager ensures that on
  59. return from real mode the segment registers contain valid selector
  60. values.
  61.  
  62. OK. Now imagine you have a Whizzo-matic Digital Scanner and Coffee
  63. Maker attached to your PC, and that the API for the driver for this
  64. box of tricks states that interrupt $68 subfunction $01 (in AX) will
  65. fill a buffer you supply (address in ES:DI) with its current state.
  66. In a real mode program you'd write something along the lines of
  67.  
  68.   procedure GetWhizzoState(var State : TWhizzoState);
  69.     var
  70.       R : Registers;
  71.     begin
  72.       R.ax := $01;
  73.       R.es := Seg(State);
  74.       R.di := Ofs(State);
  75.       Intr($68, R);
  76.     end;
  77.  
  78. No sweat. In protected mode, you have a problem in that the buffer
  79. StateBuffer is going to be in a part of memory unreachable from real
  80. mode, and indeed the value of ES is going to be a selector value not
  81. a segment value. What you'd ideally need is a chunk of memory that
  82. could be addressed from both real mode and protected mode _at the
  83. same time_.
  84.  
  85. Enter the two complementary functions GetDOSMem and FreeDOSMem. Like
  86. GetMem and FreeMem they allocate and deallocate memory via pointers,
  87. but unlike these two, they make sure that it is in DOS real mode
  88. memory. GetDOSMem allocates Size bytes of DOS memory and returns both
  89. a real mode pointer and a protected mode pointer to that same block
  90. of memory. FreeDOSMem takes the protected mode pointer to some
  91. previously allocated DOS memory, and frees it and the selector to it.
  92. Be aware that in Windows at least the amount of DOS memory is small
  93. and so you shouldn't hang on to it for longer than you need (other
  94. processes are also desirous of some of it).
  95.  
  96. Our code snippet in protected mode would become:
  97.  
  98.   procedure GetWhizzoState(var State : TWhizzoState);
  99.     type
  100.       PWhizzoState : ^TWhizzoState;
  101.       PtrRec = record Ofs, Seg : word; end;
  102.     var
  103.       R : TRegisters;
  104.       RealState,                (* Realmode pointer to buffer *)
  105.       ProtState : PWhizzoState; (* ...and Protected mode pointer *)
  106.     begin
  107.       if GetDOSMem(RealState, ProtState, sizeof(TWhizzoState)) then
  108.         begin
  109.           FillChar(R, sizeof(R), 0);
  110.           R.ax := $01;
  111.           R.es := PtrRec(RealState).Seg;
  112.           R.di := PtrRec(RealState).Ofs;
  113.           Intr($68, R);
  114.           Move(ProtState^, State, sizeof(TWhizzoState));
  115.           if not FreeDOSMem(ProtState) then (* nothing *) ;
  116.         end;
  117.     end;
  118.  
  119. Note that we have to get the Whizzo state into our own buffer first
  120. and then copy that into the program's own buffer. Note also that we
  121. free up the DOS memory immediately. Remember that as the interrupt
  122. expects a real mode buffer we pass the real mode address of our buffer
  123. to the interrupt, but we in our program use the protected mode pointer
  124. to access the memory.
  125.  
  126. However there is still a bug in this routine. It will cause a GPF
  127. within the Intr procedure because we are passing a real mode segment
  128. value in a protected mode segment (ie selector) register. We need to
  129. _directly_ call the real mode interrupt ourselves, which Intr doesn't
  130. do for us.
  131.  
  132. Enter the RealIntr procedure. It takes the same parameters as Intr but
  133. calls the real mode interrupt directly. Our routine now becomes:
  134.  
  135.   procedure GetWhizzoState(var State : TWhizzoState);
  136.     type
  137.       PWhizzoState : ^TWhizzoState;
  138.       PtrRec = record Ofs, Seg : word; end;
  139.     var
  140.       R : TRegisters;
  141.       RealState,                (* Realmode pointer to buffer *)
  142.       ProtState : PWhizzoState; (* ...and Protected mode pointer *)
  143.     begin
  144.       if GetDOSMem(RealState, ProtState, sizeof(TWhizzoState)) then
  145.         begin
  146.           FillChar(R, sizeof(R), 0);
  147.           R.ax := $01;
  148.           R.es := PtrRec(RealState).Seg;
  149.           R.di := PtrRec(RealState).Ofs;
  150.           RealIntr($68, R);
  151.           Move(ProtState^, State, sizeof(TWhizzoState));
  152.           if not FreeDOSMem(ProtState) then (* nothing *) ;
  153.         end;
  154.     end;
  155.  
  156. Not too bad, huh.
  157.  
  158. Now suppose instead that the Whizzo-matic driver returned the address
  159. of its state buffer directly in ES:DI (we don't have to provide a
  160. buffer for it at all). Our real mode routine might look like
  161.  
  162.   procedure GetWhizzoState(var State : TWhizzoState);
  163.     type
  164.       PWhizzoState : ^TWhizzoState;
  165.     var
  166.       R : TRegisters;
  167.       RealState : PWhizzoState;
  168.     begin
  169.       R.ax := $01;
  170.       Intr($68, R);
  171.       RealState := Ptr(R.es, R.di);
  172.       Move(RealState^, State, sizeof(TWhizzoState));
  173.     end;
  174.  
  175. Our first stab in protected mode would be to replace the Intr call
  176. with a RealIntr call, but the routine would fail later on the Move,
  177. because the interrupt would have returned a real mode address, _not_
  178. a protected mode one. We need a way of mapping a protected mode
  179. pointer onto a given real mode pointer. Recall that the Seg0040
  180. selector defines a protected mode selector to the $0040 BIOS data
  181. segment (and similarly SegB000, SegB800, etc) - we need something
  182. similar.
  183.  
  184. Enter the GetMappedDPMIPtr function. Given a real mode pointer to a
  185. memory block of Size bytes, it will create a (read/write) protected
  186. mode pointer to that same memory block. The FreeMappedDPMIPtr will
  187. free up the protected mode pointer so created (recall that in
  188. protected mode there are a limited number of selectors - we need to
  189. conserve this resource in our programs). So our better stab in
  190. protected mode would be
  191.  
  192.   procedure GetWhizzoState(var State : TWhizzoState);
  193.     type
  194.       PWhizzoState : ^TWhizzoState;
  195.     var
  196.       R : TRegisters;
  197.       RealState,                (* Realmode pointer to buffer *)
  198.       ProtState : PWhizzoState; (* ...and Protected mode pointer *)
  199.     begin
  200.       FillChar(R, sizeof(R), 0);
  201.       R.ax := $01;
  202.       RealIntr($68, R);
  203.       RealState := Ptr(R.es, R.di);
  204.       if GetMappedDPMIPtr(ProtState, RealState, sizeof(TWhizzoState)) then
  205.         begin
  206.           Move(ProtState^, State, sizeof(TWhizzoState));
  207.           if not FreeMappedDPMIPtr(ProtState) then (* nothing *) ;
  208.         end;
  209.     end;
  210.  
  211. The other routines in the EZDPMI unit came out of other
  212. considerations. The RealCall function calls a real mode routine
  213. directly given its address. I needed it for the DOS uppercase function
  214. - recall that interrupt $21 subfunction $38 returns a bunch of
  215. country-specific data (eg how dates are displayed, what the currency
  216. character is and where it appears when used with an amount, and so
  217. on). One of the items returned is the address of a routine that will
  218. uppercase a character in AL (we're talking characters with accents,
  219. umlauts, cedillas here - characters above #127). Well it so happens
  220. that this is a real mode routine, and has to be called in real mode.
  221. RealCall takes the roughly the same parameters as RealIntr (and Intr)
  222. - the first parameter is however the real mode address of a routine -
  223. and calls the routine in real mode.
  224.  
  225. The example above gets coded as follows:
  226.  
  227.   var
  228.     DOSUpCaseRoutine : RealProc;
  229.  
  230.   procedure SetDOSUpCaseRoutine;
  231.     var
  232.       R : TRegisters;
  233.       Buf : array [0..63] of word;
  234.     begin
  235.       FillChar(R, sizeof(R), 0);
  236.       R.ax := $3800;
  237.       R.ds := Seg(Buf);
  238.       R.dx := Ofs(Buf);
  239.       Intr($21, R);
  240.       DOSUpCaseRoutine := Ptr(Buf[10], Buf[9]);
  241.     end;
  242.  
  243.   function DOSUpperCase(Ch : char) : char;
  244.     var
  245.       R : TRegisters;
  246.     begin
  247.       FillChar(R, sizeof(R), 0);
  248.       R.al := ord(Ch);
  249.       RealCall(DOSUpCaseRoutine, R);
  250.       DOSUpperCase := char(R.al);
  251.     end;
  252.  
  253. And the Seg0040 value? Well, BPW doesn't define it, and I needed it...
  254.  
  255. The legal bit now. I am releasing this unit as freeware. In other
  256. words you don't have to pay me for using it in a compiled executable
  257. application program, but I retain all copyright in it and in the
  258. source code within. You cannot distribute the EZDPMI source with source
  259. of your own (as part of a programming library for example) without
  260. including my copyright notice and without paying money to the charity
  261. of my choice for the pleasure of doing so.
  262.  
  263. Enjoy. If you have any problems, you can get in touch with me via
  264. CompuServe on [100116,1572]. Similarly, if you'd like some extensions
  265. to it get in touch and I'll see what I can do.
  266.  
  267.                              Julian M. Bucknall, London UK, March 1993
  268.  
  269.  
  270. EZDPMI Copyright (c) 1993 Julian M. Bucknall
  271. ======================================================================}
  272.  
  273. unit EzDPMI;
  274.  
  275. {------Common compiler switches---------------------------------------}
  276. {$A+   Word align variables }
  277. {$B-   Short-circuit boolean expressions }
  278. {$F+   Force Far calls }
  279. {$I-   No I/O checking }
  280. {$N+   Allow coprocessor instructions }
  281. {$P+   Open parameters enabled }
  282. {$Q-   No integer overflow checking }
  283. {$R-   No range checking }
  284. {$S-   No stack checking }
  285. {$T-   @ operator is NOT typed }
  286. {$V-   Disable var string checking }
  287. {$X+   Enable extended syntax }
  288. {$IFDEF DEBUG}
  289. {$D+,L+,Y+  Enable debug information }
  290. {$ENDIF}
  291. {---------------------------------------------------------------------}
  292.  
  293. {------Real mode compiler switches------------------------------------}
  294. {$IFDEF MSDOS}
  295. {$E+   Enable coprocessor emulation }
  296. {$G-   8086 type instructions }
  297. {$O-   Do NOT allow overlays }
  298. {$DEFINE RealMode}
  299. {$UNDEF  ProtMode}
  300. {$ENDIF}
  301. {---------------------------------------------------------------------}
  302.  
  303. {------Protected mode compiler switches-------------------------------}
  304. {$IFDEF DPMI}
  305. {$E+   Enable coprocessor emulation }
  306. {$G+   80286+ type instructions }
  307. {$UNDEF  RealMode}
  308. {$DEFINE ProtMode}
  309. {$ENDIF}
  310. {---------------------------------------------------------------------}
  311.  
  312. {------Windows compiler switches--------------------------------------}
  313. {$IFDEF WINDOWS}
  314. {$G+   80286+ type instructions }
  315. {$K+   Use smart callbacks
  316. {$W-   No Windows realmode stack frame }
  317. {$UNDEF  RealMode}
  318. {$DEFINE ProtMode}
  319. {$ENDIF}
  320. {---------------------------------------------------------------------}
  321.  
  322. {$IFDEF MSDOS} Error - protected mode only {$ENDIF}
  323.  
  324. INTERFACE
  325.  
  326. uses WinDOS,
  327. {$IFDEF Windows}
  328.      WinProcs
  329. {$ELSE}
  330.      WinAPI
  331. {$ENDIF}
  332.      ;
  333.  
  334. {$IFDEF Windows}
  335. var
  336.   Seg0040 : word;     { To access the BIOS data area in Windows }
  337. {$ENDIF}
  338.  
  339. type
  340.   RealProc = procedure;
  341.  
  342. {=DOSGetMem===========================================================
  343. Allocates and returns the real and protected mode pointers to a DOS
  344. memory block of Size bytes in the first 1Mb. Returns true if
  345. successful, false otherwise.
  346. 21Mar93 JMB
  347. ======================================================================}
  348. function DOSGetMem(var RealPtr, ProtPtr; Size : word) : boolean;
  349.  
  350. {=DOSFreeMem==========================================================
  351. Deallocates a DOS memory block allocated with DOSGetMem. Returns true
  352. if successful, false otherwise.
  353. 21Mar93 JMB
  354. ======================================================================}
  355. function DOSFreeMem(ProtPtr : pointer) : boolean;
  356.  
  357. {=RealIntr============================================================
  358. Calls the real mode interrupt IntNo. Unlike Intr this guarantees a
  359. real mode interrupt. Intr performs a protected mode interrupt first,
  360. which the DPMI server may pass thru to the real mode interrupt.
  361. Returns true if successful, false otherwise.
  362. 21Mar93 JMB
  363. ======================================================================}
  364. function RealIntr(IntNo : byte; var Regs : TRegisters) : boolean;
  365.  
  366. {=RealCall============================================================
  367. Calls the real mode Routine procedure (must be a far procedure and
  368. return with RETF). No stack is transferred, the routine is assumed to
  369. accept its parameters from the registers.
  370. Returns true if successful, false otherwise.
  371. 21Mar93 JMB
  372. ======================================================================}
  373. function RealCall(Routine : RealProc; var Regs : TRegisters) : boolean;
  374.  
  375. {=GetMappedDPMIPtr====================================================
  376. Given a real mode pointer to a DOS memory block, returns a protected
  377. mode pointer mapped to the same block.
  378. Returns true if successful, false otherwise.
  379. 21Mar93 JMB
  380. ======================================================================}
  381. function GetMappedDPMIPtr(var ProtPtr; RealPtr : pointer; Size : word)
  382.            : boolean;
  383.  
  384. {=FreeMappedDPMIPtr===================================================
  385. Frees a protected mode pointer (ie selector) that was allocated by
  386. GetMappedDPMIPtr.
  387. Returns true if successful, false otherwise.
  388. 21Mar93 JMB
  389. ======================================================================}
  390. function FreeMappedDPMIPtr(ProtPtr : pointer) : boolean;
  391.  
  392. IMPLEMENTATION
  393.  
  394. var
  395.   ExitSave : pointer;
  396.  
  397. function DOSGetMem(var RealPtr, ProtPtr; Size : word) : boolean;
  398.   type
  399.     LI = record LoWord, HiWord : word; end;
  400.   var
  401.     RealMode : pointer absolute RealPtr;
  402.     ProtMode : pointer absolute ProtPtr;
  403.     Result : longint;
  404.   begin
  405.     Result := GlobalDOSAlloc(Size);
  406.     if (Result <> 0) then
  407.       begin
  408.         RealMode := Ptr(LI(Result).HiWord, 0);
  409.         ProtMode := Ptr(LI(Result).LoWord, 0);
  410.         DOSGetMem := true;
  411.       end
  412.     else DOSGetMem := false;
  413.   end;
  414.  
  415. function DOSFreeMem(ProtPtr : pointer) : boolean;
  416.   type
  417.     SO = record O, S : word; end;
  418.   begin
  419.     DOSFreeMem := GlobalDOSFree(SO(ProtPtr).S) = 0;
  420.   end;
  421.  
  422. function RealIntr(IntNo : byte; var Regs : TRegisters) : boolean;
  423. assembler;
  424.   type
  425.     TDPMIRegisters = record
  426.       EDI, ESI, EBP, Res, EBX, EDX, ECX, EAX  : longint;
  427.       Flags, ES, DS, FS, GS, IP, CS, SP, SS   : word;
  428.     end;
  429.   var
  430.     DPMIregs : TDPMIRegisters;
  431.   asm
  432.     push ds
  433.     lds si, Regs
  434.     mov ax, ss;  mov es, ax;  lea di, DPMIregs
  435.     cld
  436.     xor ax, ax
  437.     add si, 12         { EDI }
  438.     movsw;  stosw
  439.     sub si, 4          { ESI }
  440.     movsw;  stosw
  441.     sub si, 4          { EBP }
  442.     movsw;  stosw
  443.     stosw;  stosw      { Res }
  444.     sub si, 8          { EBX }
  445.     movsw;  stosw
  446.     add si, 2          { EDX }
  447.     movsw;  stosw
  448.     sub si, 4          { ECX }
  449.     movsw;  stosw
  450.     sub si, 6          { EAX }
  451.     movsw;  stosw
  452.     add si, 16         { Flags }
  453.     movsw;
  454.     sub si, 4          { ES }
  455.     movsw;
  456.     sub si, 4          { DS }
  457.     movsw;
  458.     mov cx, 6          { FS, GS, IP, CS, SP, SS }
  459.     rep stosw
  460.     lea di, DPMIregs
  461.     mov ax, 0300h      { DPMI code to simulate intr }
  462.     xor bx, bx         { Set BH to zero (and BL) }
  463.     mov bl, IntNo      { Save interrupt number }
  464.     xor cx, cx         { No stack words to copy }
  465.     int 31h            { DPMI Services }
  466.     mov ax, 0
  467.     jc @@ExitPoint     { Error? - yes }
  468.     les di, Regs
  469.     mov ax, ss;  mov ds, ax;  lea si, DPMIregs
  470.     cld
  471.     add si, 28; movsw  { AX }
  472.     sub si, 14; movsw  { BX }
  473.     add si, 6;  movsw  { CX }
  474.     sub si, 6;  movsw  { DX }
  475.     sub si, 14; movsw  { BP }
  476.     sub si, 6;  movsw  { SI }
  477.     sub si, 6;  movsw  { DI }
  478.     add si, 34; movsw  { DS }
  479.     sub si, 4;  movsw  { ES }
  480.     sub si, 4;  movsw  { Flags }
  481.     mov ax, 1
  482.   @@ExitPoint:
  483.     pop ds
  484.   end;
  485.  
  486. function RealCall(Routine : RealProc; var Regs : TRegisters) : boolean;
  487. assembler;
  488.   type
  489.     TDPMIRegisters = record
  490.       EDI, ESI, EBP, Res, EBX, EDX, ECX, EAX  : longint;
  491.       Flags, ES, DS, FS, GS, IP, CS, SP, SS   : word;
  492.     end;
  493.   var
  494.     DPMIregs : TDPMIRegisters;
  495.   asm
  496.     push ds
  497.     lds si, Regs
  498.     mov ax, ss;  mov es, ax;  lea di, DPMIregs
  499.     cld
  500.     xor ax, ax
  501.     add si, 12         { EDI }
  502.     movsw;  stosw
  503.     sub si, 4          { ESI }
  504.     movsw;  stosw
  505.     sub si, 4          { EBP }
  506.     movsw;  stosw
  507.     stosw;  stosw      { Res }
  508.     sub si, 8          { EBX }
  509.     movsw;  stosw
  510.     add si, 2          { EDX }
  511.     movsw;  stosw
  512.     sub si, 4          { ECX }
  513.     movsw;  stosw
  514.     sub si, 6          { EAX }
  515.     movsw;  stosw
  516.     add si, 16         { Flags }
  517.     movsw;
  518.     sub si, 4          { ES }
  519.     movsw;
  520.     sub si, 4          { DS }
  521.     movsw;
  522.     mov cx, 6          { FS, GS, IP, CS, SP, SS }
  523.     rep stosw
  524.     sub di, 8
  525.     mov ax, Routine.Word[0]  { Routine's real IP }
  526.     stosw
  527.     mov ax, Routine.Word[2]  { Routine's real CS }
  528.     stosw
  529.     lea di, DPMIregs
  530.     mov ax, 0301h      { DPMI code to simulate call }
  531.     xor bx, bx         { Set BH to zero (and BL) }
  532.     xor cx, cx         { No stack words to copy }
  533.     int 31h            { DPMI Services }
  534.     mov ax, 0
  535.     jc @@ExitPoint     { Error? - yes }
  536.     les di, Regs
  537.     mov ax, ss;  mov ds, ax;  lea si, DPMIregs
  538.     cld
  539.     add si, 28; movsw  { AX }
  540.     sub si, 14; movsw  { BX }
  541.     add si, 6;  movsw  { CX }
  542.     sub si, 6;  movsw  { DX }
  543.     sub si, 14; movsw  { BP }
  544.     sub si, 6;  movsw  { SI }
  545.     sub si, 6;  movsw  { DI }
  546.     add si, 34; movsw  { DS }
  547.     sub si, 4;  movsw  { ES }
  548.     sub si, 4;  movsw  { Flags }
  549.     mov ax, 1
  550.   @@ExitPoint:
  551.     pop ds
  552.   end;
  553.  
  554. function GetMappedDPMIPtr(var ProtPtr; RealPtr : pointer; Size : word)
  555.            : boolean;
  556. assembler;
  557.   asm
  558.     xor ax, ax               { Get an LDT descriptor & selector for it }
  559.     mov cx, 1
  560.     int 31h
  561.     jc @@Error
  562.     xchg ax, bx
  563.     xor ax, ax               { Set descriptor to real address }
  564.     mov dx, RealPtr.Word[2]
  565.     mov al, dh
  566.     mov cl, 4
  567.     shr ax, cl
  568.     shl dx, cl
  569.     xchg ax, cx
  570.     mov ax, 7
  571.     int 31h
  572.     jc @@Error
  573.     mov ax, 8                { Set descriptor to limit Size bytes }
  574.     xor cx, cx
  575.     mov dx, Size             {!!.01} {orig used cx instead of dx}
  576.     add dx, RealPtr.Word[0]  {!!.01}
  577.     jnc @@1
  578.     xor dx, dx               {!!.01}
  579.     dec dx                   {!!.01}
  580.   @@1:
  581.     int 31h
  582.     jc @@Error
  583.     cld                      { Save selector:offset in ProtPtr }
  584.     les di, ProtPtr
  585.     mov ax, RealPtr.Word[0]
  586.     stosw
  587.     xchg ax, bx
  588.     stosw
  589.     mov ax, 1
  590.     jmp @@Exit
  591.   @@Error:
  592.     xor ax, ax
  593.   @@Exit:
  594.   end;
  595.  
  596.  
  597. function FreeMappedDPMIPtr(ProtPtr : pointer) : boolean;
  598. assembler;
  599.   asm
  600.     mov ax, 1
  601.     mov bx, ProtPtr.Word[2]
  602.     int 31h
  603.     mov ax, 0
  604.     jc @@Error
  605.     inc ax
  606.   @@Error:
  607.   end;
  608.  
  609. {$IFDEF Windows}
  610. {=CleanupDPMI=========================================================
  611. Removes the Seg0040 selector for Windows.
  612. 21Mar93 JMB
  613. ======================================================================}
  614. procedure CleanupDPMI; far;
  615.   var
  616.     PP : pointer;
  617.   begin
  618.     ExitProc := ExitSave;
  619.     PP := Ptr(Seg0040, 0);
  620.     FreeMappedDPMIPtr(PP);
  621.   end;
  622.  
  623. {=Initialisation======================================================
  624. Sets up the Seg0040 selector for Windows.
  625. 21Mar93 JMB
  626. ======================================================================}
  627. type
  628.   SO = record O, S : word; end;
  629. var
  630.   PP : pointer;
  631. begin
  632.   GetMappedDPMIPtr(PP, Ptr($40, 0), $400); {1024 byte limit}
  633.   Seg0040 := SO(PP).S;
  634.   ExitSave := ExitProc;
  635.   ExitProc := @CleanupDPMI;
  636. {$ENDIF}
  637. end.
  638.