From: berend@contrast.wlink.nl (Berend de Boer)
Newsgroups: comp.lang.pascal
Subject: Pt 2/2: Protected Mode Tips
Date: Thu, 21 Jan 1993 15:52:00

<continued from previous message>

3) We will examine how to setup an interrupt for real mode. Most of    the time
this is transparantly done by Borland's DPMI Extender, but we    are on our own
now.
   The Simulate Real Mode Interrupt function needs a real mode register    data
structure. We pass this data structure to this function. Than a    switch is
made to real mode and the contents of the data structure is    copied into the
register, the interrupt is made, registers are copied    back to the data
structure, the processor switches back to protected    mode and voila: you are
in control again.
   Maybe you ask: why need I to setup such a data structure? Why can I    not
simply pass registers? Several reasons exist, but take for    example the
RealModeSeg of the previous example. You cannot simply    load a RealModeSeg in
a register. Most likely a segment violation    would occur (referring to a non
existing segment or you do not have    enough rights etc.). Therefore only in
real mode can real mode    segments be loaded.
   The data structure to pass registers between protected and real mode    can
be found in the DPMI Unit which I repeat here:

     type
       TRealModeRegs = record
         case Integer of
           0: (
               EDI, ESI, EBP, EXX, EBX, EDX, ECX, EAX: Longint;               
Flags, ES, DS, FS, GS, IP, CS, SP, SS: Word);            1: (               
DI, DIH, SI, SIH, BP, BPH, XX, XXH: Word;
               case Integer of
                 0: (
                     BX, BXH, DX, DXH, CX, CXH, AX, AXH: Word);                
 1: (
                     BL, BH, BLH, BHH, DL, DH, DLH, DHH,
                     CL, CH, CLH, CHH, AL, AH, ALH, AHH: Byte));        end;

   This looks reasonably complex, does it! More simply is the following   
structure (found in, for example, "Extending Dos" by Ray Duncan e.a.)    Offset
 Lenght Contents
   00h     4      DI or EDI
   04h     4      SI or ESI
   08h     4      BP or EBP
   0Ch     4      reserved, should be zero
   10h     4      BX or EBX
   14h     4      DX or EDX
   18h     4      CX or ECX
   1Ch     4      AX or EAX
   20h     2      CPU status flags
   22h     2      ES
   24h     2      DS
   26h     2      FS
   28h     2      GS
   2Ah     2      IP (reserved, ignored)
   2Ch     2      CS (reserved, ignored)
   2Eh     2      SP (ignored when zero)
   30h     2      SS (ignored when zero)

   In the following example, I set the registers for the above message   
exchanging function. It's best to clear all registers (or at least    the SS:SP
registers) before using them.
     uses DPMI;
     var
       Regs : TRealModeRegs;
     begin
       FillChar(Regs, SizeOf(TRealModeRegs), #0); {* clear all registers *}    
   with Regs do  begin
         ah := $xx;
         es := PtrRec(RealModeSeg).Seg;
         di := PtrRec(RealModeSeg).Ofs
       end;  { of with }
     end;
   All this is fairly standard. Just set up the registers you interrupts   
expect, very much like the Intr procedure.

4) We can now issue the interrupt in Real mode using the RealModeInt   
procedure (in the DPMI unit). Its definition is

     procedure RealModeInt(Int: Byte; var Regs: TRealModeRegs);

   Or you can call int 31h, function 0300h, see Ralph Brown's interrupt   
list.    For our message exchanging program it would simply be:
     RealModeInt(xx, Regs);

5) Examine the results. Modified registers are passed in the Regs data   
structure so you can check the results.


Unsupported interrupts which use the stack
------------------------------------------
The second type of unsupported interrupts are the ones which make use of the
stack. We can distinguish between:
1. interrupts which need parameters on the stack
2. interrupts which return parameters on the stack

1) For the first type we need a modified RealModeInt. Int 31h, function   
0300h can also copy words from the protected stack to the real mode    stack.
There is an extra compilication, which I had not told yet. As    the stack in
protected mode resides in a protected mode segment it is    unusable for the
real mode interrups. So Borland's DPMI Extender    switches from the protected
to a real mode stack (and back).    We can supply a default real mode stack of
we set the registers in    the real mode register data structure to zero.
Failure to set them up    properly could have disastrous results!
   The RealModeInt in the DPMI unit cannot be used for this situations,    we
have to go the assembler level. Say we want to copy 2 words from    the stack,
and lets say that Regs has been set up:

   uses DPMI;
   var
     Regs : TRealModeRegs;
     Value1, Value2 : word;  {* values to pass as stack parameters to *}       
                      {* the interrupt *}
   begin
     {* set up Regs here *}
     asm
       mov     ax,Value1     {* push two values on the protected stack *}      
 push    ax
       mov     ax,Value2
       push    ax
       MOV     BL,Int        {* bl contains the interrupt *}        XOR    
BH,BH         {* clear bh *}
       mov     cx,2          {* cx contains the number of words *}             
                {* which should be copied from the *}                          
   {* protected mode stack to the real stack *}        LES     DI,Regs       {*
es:di point to regs *}
       MOV     AX,0300H
       INT     31H           {* issue Simulate Real Mode Interrupt *}      end;
   end;

   Note that register cx contains the number of stack WORDS (not BYTES)    to
be copied.

2) The most difficult situation I think. A default stack (by setting    SS and
SP to zero) does not suffice. We have to set up a real mode    stack ourselves.
We can get a real mode segment by calling    GlobalDosAlloc and setting SS to
the returned segment and SP to the    size of the allocated memory. An example
is given below, where a    solution to my original problem is given.


Solution for the dv_win_me procedure:

  uses DVAPI, Objects, WinApi, WinTypes, DPMI;

  function dv_win_me : longint;
  const
    StackSize = SizeOf(longint);
  var
    Regs : TRealModeRegs;
    RealStackSeg : word;
    RealStackSel : word;
    l : longint;
  begin
  {* allocate a 1 dword stack *}
    l := GlobalDosAlloc(StackSize);
    RealStackSeg := LongRec(l).Hi;
    RealStackSel := LongRec(l).Lo;

  {* clear the stack (not necessary) *}
    FillChar(Ptr(RealStackSel, 0)^, StackSize, #0);

  {* clear all registers *}
    FillChar(Regs, SizeOf(TRealModeRegs), #0);

  {* set registers *}
    with Regs do  begin
      bx := $0001;
      ah := $12;
      ss := RealStackSeg;
      sp := StackSize;
    end;  { of with }

  {* perform real mode interrupt *}
    RealModeInt($15, Regs);
    dv_win_me := PLongint(Ptr(RealStackSel, 0))^;
  end;

  begin
    writeln(dv_win_me);
  end.


You see, code size bloats in protected mode! (Therefore you have 16MB....)


References
----------
- The usual Borland set of handbooks

- "Borland Open Architecture Handbook for Pascal", sold separately by  
Borland, 184 pages.

- "Extending Dos, a programmer's Guide to protected-mode dos", Ray   Duncan,
Charles Petzold, Andrew Schulman, M. Steven Baker, Ross P.   Nelson, Stephen R.
Davis and Robert Moote. Addison-Wesly, 1992.   ISBN: 0-201-56798-9

- "PC Magazine Programmer's Technical Reference: The Processor and  
Coprocessor", Robert L. Hummel. Ziff-Davis Press, 1992.
  ISBN: 1-56276-016-5


Berend
fido: 2:281/527.23
email: berend@contrast.wlink.nl