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!