Friday, February 21, 2020 | Toby Opferman

Protected Mode Introduction Part 1

Toby Opferman

                        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.

  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.

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.
       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

  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
  NoError: MOV EAX, 1

  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

  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!
About Toby Opferman

Professional software engineer with over 15 years...

Learn more »
Codeproject Articles

Programming related articles...

Articles »

Resume »

Email: codeproject(at)opferman(dot)com