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

Hello All,

Compiling real to protected mode has been very simple for most of us. Just
compile and go ahead. 99.5% of your code works fine. But the other 0.5% is
going to give you some hard, hard work. In this article I describe first how I
first stuck on the protected stone. Than I try to give a general overview of
problems one might encounter when using interrupts. Next I describe the
solutions or give at least some hints. At last I give a solution to the
original program which made me aware of protected mode conversion problems.
References can be found at the end of this article. Of course, all disclaimers
you can come up with apply!


When compiling a big program, which supported DesqView, a GP fault occurred. It
was simple to trace the bug down: TDX would show me the offending code. You can
get the same error if you try to run the following program in protected mode:

========cut here==========================
program Test;

function  dv_win_me : longint;  assembler;
asm
  mov    bx,0001h
  mov    ah,12h
  int    15h       {* push dword handle on stack *}
  pop    ax        {* pop it *}
  pop    dx        {* and return it *}
end;

begin
  writeln(dv_win_me);
end.
========cut here==========================

This little program must be run under DesqView. When run under DesqView it
returns the current window handle on the stack. BUT: when compiled under
protected mode NO dword handle is returned on the stack. So a stack fault
occurs.

What happened? I stuck on one of those unsupported interrupts. Only supported
interupts guarantee to return correct results. You can find a list of all
supported interrupts in the Borland Open Architecture Handboek for Pascal,
Chapter 2 (seperate sold by Borland, not included in your BP7 package).
Supported are the general Dos and Bios interrupts.

Before eleborating on supported and unsupported interrupts, I have to explain a
few issues which are probably new to us Pascal programmers. Whenever a user
interrupt occurs in protected mode (you issue a int xx call) Borlands DPMI
Extender switches to real mode, issues the interrupt, and switches back to
protected mode.
This works find for most cases: interrupts which only pass
register parameters work fine. But what happens if you, for example, called the
Print String function? (int 21h, ah=09h). You pass it a variable from your Data
segment. But, this data segment resides in protected mode. And the DS register
contains a selector. Dos can't work with this, it needs segments, not
selectors, and the data needs to be below the 1MB boundary.
So Borland's DPMI Extender does more: it translates selectors to segments when
appropriate. But, it can only do so for interrupts it knows that they need a
translation. Such interrupts are called supported. Interrupts about which
Borland's DPMI Extender does not know about are unsupported. And they are going
to give you real problems!

So you see, when only some data are passed in registers, everything works fine.
Selectors cause problems. But why did the above program not work? It didn't use
selectors you might ask. Well, there is another set of interrupts that are
unsupported: those that return values on the stack. This is the case with the
above program.

So, to conclude:
* supported interrupts
  - simple parameter passing using registers, no segments/selectors     or
stacks included
  - interrupts which Borland's DPMI Extender knows about (too few for     most
of us)
* unsupported interrupts
  - using segments/selectors
  - involving stacks

I will give code of how to fix both types of problems. Usefull is the DPMI
unit, which comes with the Open Architecture Handbook. You do not need this
unit, simply looking the interrupts up in Ralph Brown's interrupts list and
writing functions/procedures for them, works fine.


Unsupported interrupts which need segments
------------------------------------------

Because the data segment and stack segment reside in protected mode, you need
to allocate memory in real mode, copy your data and issue the interrupt by
calling the DPMI Simulate Real Interrupt. So our to-do list is:
1) allocate real mode memory
2) copy data from protected mode to real mode
3) set up the real mode registers
4) issue interrupt
5) examine results

1) You can allocate real mode memory by issueing a GlobalDosAlloc (not   
referenced the online help, although in the manuals) request. The   
GlobalDosAlloc is in the WinApi unit. For example:
     uses WinAPI;
     var
       Return : longint;
       MemSize : longint;
     begin
       MemSize := 1024;
       Return := GlobalDosAlloc(MemSize);
     end;
   This call allocates a block of memory, 1K in size, below the 1MB   
boundary. The value in Return should be split in LongRec(Return).Lo    and
LongRec(Return).Hi. The Hi-order word contains the segment base    address of
the block. The low-order word contains the selector for    the block.

2) You use the selector to acces the block from protected mode and you    use
the segment of the block to acces the block from real mode (your   
interrupt).
   For example: we wanted to exchange messages with some interrupt. The    code
for this would be:
     uses WinAPI;
     var
       Return : longint;
       MemSize : longint;
       RealModeSel : pointer;
       RealModeSeg : pointer;
       Message : string;
     begin
       MemSize := 256;
       Return := GlobalDosAlloc(MemSize);
       PtrRec(RealModeSel).seg := LongRec(Return).Lo;
       PtrRec(RealModeSel).ofs := 0;
       PtrRec(RealModeSeg).seg := LongRec(Return).Hi;
       PtrRec(RealModeSeg).ofs := 0;

     {* Both RealModeSel(ector) and RealModeSeg(ment) point to the same        
absolute address now. *}

     {* move message below 1MB using a selector *}
       Message := 'How are your?';
       Move(Message, RealModeSel^, SizeOf(Message));

     {* issue interupt, explained below *}
     { <..code..> }
     {* the interrupt returns a message *}

     {* move interrupt's message below 1MB to protected mode *}       
Move(RealModeSel^, Message, SizeOf(Message));
       writeln(Message);     {* "yes, I'm fine. Thank you!" *}      end;

<...continued in next message...>