Due to popular demand, a few hints how to manage 32 bit pmode with
Delphi 2 and DOS.

1. How is the adress space organized?

Hmm... where to start?

Well, the new thing on a 386+ CPU is a thing called "paging".

To understand paging you need to understand that there is a difference
between LINEAR and PHYSICAL addresses. Physical addresses are those used
by the CPU to address physical memory on your main board. If there was
no paging, they would map identically to linear address space, right?

Now, assume, there actually _is_ paging enabled. This would mean, that
ANY address your program generates while reading/writing memory or
executing code is a linear address that would not neccessarily be the
equivalent of the "real", physical address. The CPU uses some sort of
an address translation table (which is maintained by the OS or the DOS
extender) to generate physical addresses.

This, BTW, is how it is possible to run more than one DOS box at a
time in Windows. Every DOS box has a linear address space starting
at linear address 00000000.

It's obvious that they cannot map to the same physical memory, isn't it?

However, this is not a thing you have to worry about unless you decide
to write an operating system or a DOS extender or such...

Now we know how it's possible to have the full 4G address space for
each application in flat memory model as it is used in WIN 95/NT.
This is done by giving each application an unique set of those
address translation tables.

What about the segment registers, you may ask?

Well, in WIN 95/NT they are loaded with descriptor values that
have a linear base of 00000000 also. (this applies to CS:,DS:,SS: and
ES:. FS: usually too, but there are things like TLS I do not want to go
deeper into...)

So every offset address (a pointer in Delphi 2 is NEAR and nothing else
than an offset) is a linear address too.

Things get different while running under a DOS extender. The base
adresses of CS:, DS:... are identical though, but they are not 00000000.

Even worse, the linear start address may change during program
execution. This is, because the DPMI host is allowed to move the
segment around in memory if you decide to resize it. Resizing the
segment may (but need not) happen anytime a heap object is
created/destroyed. The above section about paging may already have
told you that there is no physical memory moved around.
The OS merely modifies the address translation tables for the
current process.

You also don't have to take care of this fact unless you want to do
direct memory read/write (VIDEO...), since the DOS extender is managing
anything else for you in almost no time.

2. But what if I actually _want_ to do direct mem and stuff...?

Well, first of all, I suggest, you grab the DPMI 0.9 spec from somewhere
so a lot of questions will be self- answering.

A good place is:

ftp://x2ftp.oulu.fi/pub/msdos/programming/specs/dpmispec.arj

Problem here is that I'm instantly thinking in terms of Assembler, not
Pascal as a majority of you may do. So there are cases where I would
say "no problem at all" and hacked in the ASM straight from mind.

So, my explaination may appear a bit confusing to others. Anyway, keep
in mind that there is almost no problem that cannot be solved with inline
assembly!

However, personally I use to create a descriptor that actually has a base
of 00000000 and a limit of 4G so I can access all memory using offsets
only. If you like to know how this is done, have a look into CRT.PAS.
If you get no idea anyway, just move the "huge_selector" thing from
"implementation" to "interface" and it will be public.

(SECRET HINT: I've done this for you already, have a look at the source code...
 Psssttt, don't tell anybody... :)

Now, as an example, instead of saying:

mem[$A000:0000]:=color;

just say:

asm
 push gs
 mov  gs,huge_selector
 mov  edx,0a0000h
 mov  al,color
 mov  gs:[edx],al
 pop  gs
end;

-------------------------------------------------------------------------------

				IMPORTANT NOTE!

-------------------------------------------------------------------------------

RECENTLY I WROTE IN THIS PLACE:

>Here's the well known putpixel in mode $13:
>
>procedure putpixel (X,Y: Integer; Color: Byte);
>begin
>  asm
>    mov	eax,Y
>    mov edx,X
>    shl	eax,6
>    push gs
>    add edx,eax
>    mov	gs,huge_selector
>    mov	cl,Color
>    mov	gs:[edx+eax*4+0a0000h],cl
>    pop	gs
>  end;
>end;

THIS ONE IS VERY LIKELY TO CRASH!!!

Take my apologies for not testing it first. The misbehaviour is because of a
compiler bug not processsing immediate offsets correctly when the low word is
set to zero. So the immediate 0a0000h is very likely to be ignored by the
compiler. 

Use DejaNews to parse COMP.LANG.PASCAL.DELPHI.MISC for a discussion of this.

The subject line was: "D2 sux! (inline asm bug... ) AAARRRGHHH!!!"
(without the quotes)

Furthermore, see the file PUTPIXEL.PAS for a version that actually works!

It actually may (and, in most cases, will) work to load GS: with
the selector once and use it all the time, so you save the cycles
spent on saving/loading/restoring a segment register in PM.

You can load even ES: and FS: with another selector, yeah, you even
could change DS: if only the local stack is dereferenced, but you _will_
have to save/restore them since DS:, ES: and FS: are used during program
execution while GS: usually isn't.

LINEAR FRAME BUFFER (VESA)

This one may be interesting for some of you since you couldn't do it
in 16 bit pascal. I've not tried it yet since I have no VESA 2.0 card
here (shame on me), but there's no reason why it shouldn't work:

- Use DPMI "MAP PHYSICAL REGION" with base address and size of the LFB
  as you received it from VESA BIOS, a linear address will be returned.

- Now you have the choice: use either the above huge_selector and the
  linear address as an offset or create a new descriptor to access the LFB.


3. Pointer casting

In Delphi 2 a pointer isn't a Segment:Offset thing anymore, its merely
a dword. So we can do all that neat pointer arithmetic stuff C programmers
are doing. However, the compiler will complain if one tries something like:

var	i:integer;
	p:pointer;
...

i:=p;
i:=i+some_offset;
p:=i;

Well, but be are free to say:

asm
  mov	eax,p
  add	eax,some_offset
  mov	i,eax
  mov	p,eax
end

Hope this helps a bit.


Wuschel
