Toby Opferman
http://www.opferman.net
programming@opferman.net
Protected Mode Tutorial
This is for those who already know Real Mode Programming. We start
off simple. We will start off with Memory in Pmode, go onto Assembly
instructions, go from there to DPMI and from there to Direct Pmode &
how to set pmode manually. There are two differnt kinds of Pmode. There
is Flat and Segmented. In this tutor we will talk about Pmode in general.
This is not a Detailed Tutorail, this is a Quick n Dirty Tutorial made for
people who want to get right into Pmode w/o any hassels and can pick
up information quickly.
In real mode, you note that Memory is addressed by 16bit Segments
and a 16-bit offset. This allows you to access only 1 Meg of memory
(W/O the use of XMS/EMS). In Protected mode, you can access 4 Gig segments.
To Get the Pmode address of a Real Mode location in memory.
PmodeAddress = SEG*16 + Offset.
Example:
Let's say you used DOS4GW Extender (Flat Model, which just lets
you access everything in one big chunk) and you wanted to get the 8x8
Font out of Ram. F000:FA6E is the location. To get the Pmode location
you must (F000 * 16) + FA6E. Now you have the Pmode Address.
FFA6Eh
another Exmaple:
Video Segment is A000:0000
Pmode Address is A0000
You could also just SHIFT the segment Left by 4 bits.
Assembly is a bit differnt in Pmode. There are a lot of new commands
and a lot of new SHIT that you can do with some old commands. We will not
go into this that much, we will just go into the new style of programming.
First, You don't TOUCH any segment registers
ES, DS, CS, FS, GS.
Those are ONLY to be touched by the Operating System (Or DOS Extender).
To acces memory in your Segment:
MOV EDI, 0A0000h
MOV [EDI], AL
For the most part, if you are using a DOS extender, they are all pointing
to the same segment (ES=CS=DS) So you don't have to worry about casting
any of the Memory Operations. If you were designing an OS you would have to
know more details about this.
*DPMI* (Dos Protected Mode Interrupt)
Dos Extenders Like DOS4GW use this.
You may not call any 16-Bit Real Mode Interrupt that has 16-bit code.
You will have to use the Register Structure and allocate Low Dos Memory.
An interrupt that takes memory as input or returns it will have to be called
through interrupt 31h. Some interrupts (Though you are still not supposed
to) you may get away with calling them directly. Such are any interrupt
that does not take a pointer to a block of memory or return one.
EXAMPLE:
MOV AX, 13h
INT 10h
Setting Mode 13h can be called just like the way above.
The Structure (In C)
/* Real Mode Registers Structure */
typedef struct rmode_type {
long EDI;
long ESI;
long EBP;
long reserved;
long EBX;
long EDX;
long ECX;
long EAX;
short flags;
short ES, DS, FS, GS, IP, CS, SP, SS;
} RMI, *RMIPtr;
Function 1 Subfuntion 0h
Allcoate Low Dos Memory.
It will return a Segment into the allocated memory.
The offset of the memory will always be 0.
MOV EBX, Size Of Memory To Be Allocated
MOV EAX, 100h ; Allocate Dos Memory
ADD EBX, 15 ; Add 15 To The Size
SHR EBX, 4 ; & Shift Over By 4 (Memory Can only be alloced in 16 Byte
; Pages. Thus if you wanted 1 byte of memory, you would
; actually allocated 16 bytes
; 1 + 15 = 10h >>4 = 1 page (16 Bytes).
INT 31h ; Call Interrupt 31h
JNC SHORT Done ; No Carry, then Allocated
XOR AX, AX ; No Memory Free (Set AX = 0
If AX = 0 here, then memory was not allocated.
DX = Selector for Memory (We Will talk about Selectors later)
Save the Selector for the Memory for when you deallocate
the Memory.
To Set The object (In C) To the memory,
Let's say you have a Object of type struct Thing.
struct Thing *ThingObject;
short Selector, Segment;
Segment = AllocateDosMemory(&Selector, sizeof(Thing));
ThingObject = Segment<<4;
Now, you shift the Segment by 4 and assign that value to your ThingObject.
Because Seg*16 + Offset and your offset will be 0.
Save the Selector for when you deallocate memory.
Now, when writting to the RMI strucutre:
Let's say for example that ThingObject was VESA Information structure.
And you wanted to call Function 4Fh subfunction 1 of INT 10h
RMI X;
X.EAX = 0x4F01; // Set the Function/Subfunction
X.ECX = 0x101; // CX = SVGA Mode
X.EDI = 0; // ES:DI Points to VESA structure
X.ES = Segment;
That is how you set up the structure.
MOV EDI, Address of X
MOV EBX, 10h ; Real Mode Interrupt To Call
PUSH ES ; Save ES
MOV EAX, 300h ; Function 3h SubFunction 0 - Call Real Mode INT
XOR ECX, ECX
INT 31h
JNC SHORT NoError ; Carry Flag Set? There is Error.
XOR EAX, EAX ; Error, Set EAX = 0
JMP SHORT Done
NoError: MOV EAX, 1 ; No Error, set EAX != 0
Done:
POP ES
If EAX = 0, Then there was an error.
Now your structure should have the information the interrupt
was supposed to put in it.
To Free The Memory, All you need is the Selector.
MOV DX, Selector ; The Selector From the allocation
MOV EAX, 101h ; Function 1h Sub Function 1
INT 31h ; Cal DPMI
JNC SHORT NoError ; If Carry Set, Then Error
XOR EAX, EAX
JMP SHORT Done
NoError: MOV EAX, 1
Done:
If EAX = 0, Then Error Deallocating Memory.
Remeber, the Above is only used to allocate low dos memory,
and the only purpose to allocating low dos memory is if you
need it to call a DOS Real Mode Interrupt.
A Selector is a number that tells what entry in the LDT or GDT you
are selecting. The LDT and GDT hold information about the memory allocated
(Base & Size for example). That is why it only needs the Selector to
deallocate the memory.
LDT is Local Descriptor Table.
GDT is Global Descriptor Table
IDT is Interrupt Descriptor Table.
These tables hold information about memory.
GDT is Global, throughout all programs running, etc. This is
the OS's table.
LDT is local, every program running has it's own set of LDT's.
IDT is for Interrupts, where they are in memory.
These kinda act like the Interrupt Vector Table in DOS but they are
relocatable.
I am not going to go into detail on these. The only reason to know
about these is to Create an Operating System (Or a DOS Extender).
If you want to know more about these, get a book:
Protected Mode Software Artitecture
By Addison-Wesley Publishing Company
(Part of the PC Architecture Series)
ISBN - 0-201-55447-X
This tutorial was only meant as a primer.
And, as I promised, here is how to set Pmode Directly:
MOV EAX, CR0 ; Get Control Register 0
OR AL, 1 ; Set Pmode Bit
MOV CR0, EAX ; Restore Control Register 0
And that is it! (But don't try that w/o setting an LDT, IDT and GDT and
w/o detecting to make sure you're running a 386+ processor!)
Good Luck!