Hi,

GDT data format is ( per entry):
   bits 0-15: low 16 bits of limit
   bits 16-39: low 24 bits of segment base
   bits 40-63: type
   bit  44:    1 = memory segment, 0 = system segment
   bits 45-46:  privilege level
   bit  47:    1 = present , 0 = absent
   bits 48-51: upper 4 bits of limit ( limit is only 20 bits)
   bit 52:     available for software use
   bit 53:     unused, must be 0
   bit 54:     segment type: 1 = 32 bit, 0 = 16 bit
   bit 55:     granularity: 1 = limit is page granular ( 4096 bytes),
				0 = limit is byte granular
   bit 56-63:  upper 8 bits of segment base

   for  memory type selectors (bit 44 = 1) type is one of:
 	0: read only
        2: read-write
        4: read-only, expand down limit
        6: read-write, expand down limit
        8: execute-only
        A: execute/read
        C: execute-only, conforming
        D: execute-read, conforming
   for  system type selectors ( bit 44 = 0) type is one of:
	2: LDT
	5: Task gate
	9: Available 386 TSS
	B: Busy 386 TSS
	C: 386 call gate
	E: 386 interrupt gate	( hardware interrupts)
	F: 386 trap gate	( software interrupts)

IDT data format is( per entry):
   bits 0-15: offset low 16 bits (eip)
   bits 16-31: 386 selector to use (CS)
   bits 32-36: Amount of data to copy between rings.  Means nothing
		for a hardware interrupt
   bits 37-39: unused, must be 0
   bits 40-43: type
   bit 44:	should be 0   to indicate a system type
   bits 45-46:  Priviledge level software interrupts are allowed at
   bit 47:     1 = present, 0 = absent.  Absent causes a trap
   bits 48-63: upper 16 bits of offset

   type is one of:
	5: task gate
	E: 386 interrupt gate (hardware interrupts)
 	F: 386 trap gate (software INT instruction)
   Trap gates don't disable interrupts, interrupt gates do.


Page table data format is ( per entry):
   bit 0 : 1 = present, 0 = absent
   bit 1 : 1= writeable, 0 = readonly
   bit 2 : 1 = user mode, 0 = can't access at priviledge level 3 (supervisor mode)
   bit 3,4: unused, should be 0
   bit 5,6: status bits, can be used by virtual memory systems
   bit 7-8: unused, should be 0
   bit 9-11: available to software
   bit 12-31: Upper 20 bits of the data location.  Data page tables
	refer to is always allocated on 4K boundaries.

Page table heirarchy is two-level.
   At the top level is the page directory.  It fits in one page.
   Each entry refers to a page table which maps up to 4MB of data.
   There are 1024 entries in the page table, this means with a two-level
   heirarchy you can map up to 4G.
   In the second-level page table, each entry maps a 4096-byte page
   There are 1024 entries, this means 1 page table = 4MB of memory.

   To perform paging, the processor takes the linear address formed by
   adding the memory location in question to the base of the segment in which 
   it is referred, then uses the upper 10 bits * 4 as an index into the
   page directory.  This entry in the page directory is used to find
   the page table which corresponds to the data, assuming the entry is
   present.  If it's absent you get a fault.  The next ten bits * 4 is
   used as an index into the page table which was found.  If this entry
   is present, the processor uses the upper 20 bits of it as a base physical
   address, then adds the lower 12 bits of the linear address to get
   the total physical address.

If you start playing with priviledge levels, just be aware that ring
transitions ( from a high priviledge level to a low one ) can cause a
stack switch to a more protected stack.  Part of the information stored
on that stack will be the SS:ESP you were at previously, so when a switch
to the outer ring is performed they can be restored.

Some 80386 exceptions save all the normal interrupt data on the stack ( including
SS:ESP if required) and additionally save an error code on the stack
which you are expected to parse.  Exception 8,10,11,12,13,14 fall in this
category.

Some of the things I do in this program may not be obvious, if you have
any questions e-mail me the code segment you are curious about and your
question.

This program is intended for compile by TASM 4.0.  If you use MASM
you will probably get two errors; MASM doesn't understand the word 'small'
in certain contexts.  Find the lines with the errors and remove the
word 'small'.

The program gets into protected mode, uses paging to access the screen wherein
it displays a bunch of '!' characters, then waits for two seconds by
using the timer interrupt as a counter.  Then it turns off paging, resets
everything for real-mode operation, and exits.

This won't work with EMM386 installed.  It also ignores the a20 line,
which may cause problems with extended memory.

David

Program follows:
   
;
; boot386.asm
;
; Programmer: David Lindauer
; Date: June 14,1995
; email address: gclind01@ulkyvx.louisville.edu
;
; Get into protected mode, enable paging, do something useful, get out again
;
;
; For assembly with TASM, MASM will give 2 errors which can be fixed
;   by deleting the word SMALL in front of DGROUP on the lines the errors
;   occur.
;

    .MODEL SMALL
    .386P

;---------------------------------------------------------------------
; Create a GDT entry for the initializer to use
;
;  base = base address
;  limit = segment size
;  type = segment type
;  flag = gdt initializer flags (used by program)
;---------------------------------------------------------------------
GDTENTRY    MACRO   base,limit,type,flag
    dw  flag
    dw  limit AND 0ffffh
    dw  base AND 0ffffh
    db  (base SHR 16) AND 0ffh
    dw  type OR ((limit SHR 8) AND 0f00h)
    db  base SHR 24
ENDM

;---------------------------------------------------------------------
; Various symbols used to make things more readable.
;
;---------------------------------------------------------------------

NUMGDT  = 6     ; Number of entries in GDT.

IDTSIZE = 800h
GDTSIZE = NUMGDT*8
gdtzerooffset = 1
gdtlastentry = 2

;
; Selectors, RPL = 0, in GDT
;
; These first two are used by the protected mode program
CS386   EQU 8       ; 386 code segment selector
DS386   EQU 10h     ; 386 data segment selector
;These next two are 8086 compatible code segments, only needed
; for switching back to real mode
CS8086  EQU 18h     ; 8086 code segment selector
DS8086  EQU 20h     ; 8086 data segment selector
;
; This is used to map physical memory into linear memory.
; It depends on paging, but if paging is 'see-through' then
; the 8086 memory will show up address 0 and extend through 1MB
;
DSABS   EQU 28h     ; Absolute data segment selector

;---------------------------------------------------------------------
; Define ordering of segments.
;
;---------------------------------------------------------------------
DGROUP  GROUP   seg8086,seg386

SEG8086 SEGMENT dword USE16 public 'CODE'
SEG8086 ends

SEG386 segment dword USE32 public 'CODE'
SEG386 ends

ABSDATA segment dword USE32 at 0
ABSDATA ends

;---------------------------------------------------------------------
; Define a 1K entry stack.
;
;---------------------------------------------------------------------

    .STACK 4096
tos LABEL   BYTE


;---------------------------------------------------------------------
; IDT and GDT are first so they will be DWORD aligned
;
;---------------------------------------------------------------------
SEG386 SEGMENT
;
; The constructed GDT
;
tGDT    db  8 DUP(-1)   ; Protected mode GDT
    db  GDTSIZE-8 DUP(0)
;
; The constructed IDT
;
; We're not going to initialize either the PICS or the IDT entries for
; 386 traps and hardware interrupts.  So if they come in, the program
; crashes
;
tIDT	db IDTSIZE DUP (0)
;
; The value to load with the lgdt reg
; 
pGDT    dw  GDTSIZE-1   ; Protected mode GDT register
gdtadr  dd  offset tGDT
;
; The value to load with the lidt command
pidt	dw IDTSIZE-1
idtadr	dd offset tIDT
;
; The base of the IDT for real-mode use
;
ridt	dw 03ffh
	dd 0
	align
;
; Assembler will assemble starting at address 0.  This can be loaded
; at an arbitrary physical address, so we maintain the value 'zero'
; which is the difference between the base of DS386 and physical 0
;
zero        dd  0   ; Offset of Protected mode from absolute zero
;
; The TIMER interrupt will increment this counter, when it hits 36
; we'll exit the program ( 2 seconds)
;
timer	dd 0	; Initial value of timer
;
SEG386 ends

ABSDATA segment
;
; The placement of the page tables is arbitrary.  They have to be aligned
; on 4K boundaries, however.  I chose to write over memory assuming it is
; unused to make the coding easier.  However, you might need to actually
; allocate the memory somehow or you could put the page tables up in
; extended memory
;
    org 90000h
;
;
; Page directory goes here
; Each page dir entry points to a page table which maps 4MB, thus with
; 1024 page tables and the page dir you can map 4GB.  The low bit is 0
; to disable the page table entries and cause a paging fault, 1 to enable
PageDir dd 1024 DUP (?)
;
; We'll map the first 16 MB of physical memory into the first 16 MB of physical
; memory ( Each page table covers 4MB in 4096 byte segments )
PageTables dd 1024 * 4 DUP (?)
;
; We're going to map the video memory at a000:0-c000:0 to memory at
; 80000000h via paging.  We'll later need to access it with a data segment
; We can either use DS386, which we have to subtract 'zero' from the
; offset 8000000h so that segmentation will map us to the right address.
; Or we can use DSABS, which has a base address of 0 and thus
; segmentation will be see-through.
VideoPageTable dd 1024 DUP (?)

ABSDATA ends


;---------------------------------------------------------------------
; Main program entry point. The first thing we do is switch to 
; protected mode. Then we call the main control program. If and when 
; the control program returns, then we return to real mode and 
; return to our caller.
;
;---------------------------------------------------------------------

seg8086 SEGMENT
    assume cs:dgroup
bootstrap:
    cld
    cli                         ; Interrupts clear throughout

    mov bx,cs                   ; Set ES=DS=CS=DGROUP
    mov ds,bx
    mov es,bx

    ;
    ; Fixup the GDT with our current location in memory.
    ;
    mov [word ptr zero],bx
    shl [zero],4                ; Fix ZERO address
    mov ebx,[zero]              ; Load it
    mov dl,[byte ptr zero + 2]  ; Load 64K segment
    add [gdtadr],ebx		; GDT and IDT regs must have a physical value
    add [idtadr],ebx

    mov esi,offset iGDT         ; Load GDT table pointers
    mov edi,offset tGDT + 8

    ;
    ; Create the GDT
    ;
    mov cx,NUMGDT-1
gdtinit:
    lodsw                       ; Get flags
    movsd                       ; Move GDT entry to table
    movsd
    test    ax,gdtZeroOffset    ; Adjust this entry?
    jz  short nooffset          ; No, continue
    add [word ptr di+2-8],bx    ; Else add in the ZERO offset
    add [byte ptr di+4-8],dl    ; to make the segment base address (seg addr 0)
				; be the physical offset of the program
nooffset:
    test    ax,gdtLastEntry
    jnz short endGDT
    loop    gdtinit             ; But don't overflow table
endGDT:

    ; Load the GDT descriptor
    lgdt    [fword ptr pGDT]
    ; Load the IDT descriptor
    lidt    [fword ptr pIDT]

    ;
    ; Switch to protected mode.
    ;
    mov ebx,CR0     ; Set protected mode
    inc ebx
    mov CR0,ebx

    ;
    ; Perform an inter-segment jump to our protected mode code. This 
    ; is necessary to ensure that CS is reloaded with a protected 
    ; mode selector. This jump has to be 'hand coded' as it is not 
    ; supported by the assembler because its a very dangerous thing 
    ; to do unless you know what your doing.
    ;
    db  066h        ; Far jump into protected mode
    db  0eah
    dw  SMALL DGROUP:ProtectedMode
    dw  0
    dw  CS386        ; Here we tell it to switch to prot mode seg
seg8086 ends         ; Exit real mode code segment


seg386 SEGMENT
;---------------------------------------------------------------------
;
; GDT entry definitions
;
;---------------------------------------------------------------------
iGDT    LABEL   WORD
;
; First two entries: base = physical start of program.
;    limit = entire address range ( 4G)
;    These are 32 bit segments
;    They are present, memory class descriptors
;    one is a read-exec code seg, the other a read-write data seg
;
    GDTENTRY    0,0fffffh,0c09Ah,gdtZeroOffset  ; 386 code
    GDTENTRY    0,0fffffh,0c092H,gdtZeroOffset  ; 386 data
;
; Next two entries: base = physical start of program
;    limit = 64K
;    These are 16-bit segments
;    One is a read-exec code seg, the other a read-write data seg
;
    GDTENTRY    0,00ffffh,9Ah,gdtZeroOffset ; 8086 code
    GDTENTRY    0,00ffffh,92h,gdtZeroOffset ; 8086 data
;
; Next entrie: base = physical 0
;   Limit = 4G
;   It's a 32-bit segment
;   It's a read-write data segment
;   Note that zer0-offsetting is not applied
    GDTENTRY    0,0fffffh,0c092h,gdtLastEntry   ; Absolute
;---------------------------------------------------------------------
; Protected mode start point. When we get here we have to reload all 
; of the selectors to ensure we do not get any protection faults 
; when we use them.
;
;---------------------------------------------------------------------
ProtectedMode   LABEL BYTE
    mov ebx,ss          ; Readjust stack for protected mode
    shl ebx,4
    movzx   esp,sp
    add esp,ebx
    mov bx,DS386        ; Protected mode stack segments
    mov ss,bx
    sub esp,ss:[zero]
    mov ds,bx           ; DS,ES = primary data segment
    mov es,bx
    mov bx,DSABS        ; FS,GS = Absolute zero data segment
    mov fs,bx
    mov gs,bx
    call initpagetables
    call initinterrupt
    ;
    ; Turn paging on
    ; This assumes linear and physical addresses for this program segment
    ; match, i.e. that paging is see-through at these addresses
    ;
    mov eax,offset PageDir ; Set the page directory base register
    mov CR3, eax
    mov eax,CR0		; Set the paging enable bit to turn paging on
    or eax,80000000h	;
    mov CR0,eax

    ;
    ; Here we are in protected mode, with a sensible environment
    ; set up in the selectors.
    ;

protection:
    call userprog
endprotection:
    cli
    ; 
    ; Turn paging off by resetting the page enable bit
    ; Again, physical and linear addresses must be the same for
    ; this segment of code
    ;
    mov eax,cr0
    and eax,7fffffffh
    mov cr0,eax

    ;
    ; Prepare to return to real mode.
    ;

    ;
    ; Reload our selectors with real mode compatible segment values.
    ;
    add esp,[zero]      ; Stack adjust
    ror esp,4
    mov ebx,DS8086      ; Real mode compatible data segments
    mov ds,ebx
    mov es,ebx
    mov fs,ebx
    mov gs,ebx
    mov ss,ebx

    ;
    ; Perform an inter-segment jump to our 16 bit protected mode 
    ; code. This is necessary to ensure that CS is reloaded with 
    ; a 16 bit segment selector. This jump has to be 'hand coded' as 
    ; it is not supported by the assembler because its a very 
    ; dangerous thing to do unless you know what your doing.
    ;
    db  0eah        ; Jump to 8086 compatible segment
    dw  SMALL DGROUP:RealMode1
    dw  0
    dw  CS8086      ; Switch to real mode compatible seg

    ;
    ; Switch to Real mode.
    ;
RealMode1:
    mov ebx,CR0     ; Back to real mode
    dec ebx
    mov CR0,ebx

    ;
    ; Perform an inter-segment jump to our real mode code. This is
    ; necessary to ensure that CS is reloaded with a real mode 
    ; segment value. This jump has to be 'hand coded' as it is 
    ; not supported by the assembler because its a very dangerous 
    ; thing to do unless you know what your doing.
    ;
    db  0eah        ; Load CS with real mode segment value
    dw  DGROUP:RealMode2
    dw  dgroup
seg386 ends

seg8086 segment

RealMode2:
    ;
    ; Restore our segment registers to GS=FS=ES=DS=CS=DGROUP
    ;
    mov bx,dgroup
    mov ds,bx
    mov es,bx
    mov fs,bx
    mov gs,bx
    sub esp,10h         ; Finish adjusting stack
    mov ss,sp           ; Leave at least 256 bytes for 8086
    shr esp,28
    add sp,100h
    lidt [fword ptr ridt] ; Load the real-mode idt up

    ;
    ; Here we are back in Real mode with our segment registers set 
    ; back to something sensible.
    ;

    mov ah,04ch
    int 21h
endprog:
SEG8086 ends
SEG386 segment
;
; Create all page tables
; We are mapping lower 16MB on a see-through basis.  You really only have
; to make sure that the routine that turns paging on and off is see-through
; although it is convenient to leave more than that see-through so you
; can access common data.  This paging routine is going to leave the
; video memory mapped in two places; you can access it either at 0a0000h
; or at 80000000h.
;
initpagetables proc
    push ds
    push es
    push fs
    pop ds 		; Switch to the absolute data segment
    push fs
    pop es
    ;
    ; First create the page tables that will allow 'see-through'
    ; access to the lower MB
    ;
    mov edi,offset PageTables
    ; The lower three bits of each page table entry are:
    ;  bit 0 = 1 means the table is accessible
    ;  bit 2 = 1 means it is a writable page
    ;  bit 3 = 1 means it is accessable at any priviledge level
    mov eax, 16 * 1024 * 1024 + 7-4096; Physical address 16MB-4096
                                      ; + lower 3 bits set
    mov ecx, 1024 * 4	; 4 tables of 1024 entries each of which
			; allocates 4096 bytes is 16 MB
fill16mb:
    mov [edi+ecx*4-4],eax; Fill in the table entry
    sub eax,4096	; Move to offset of next lower page
    loop fill16mb
    ; 
    ; Now create the page table where we map 0a0000 to 80000000h
    ; It will be 128K long, so we only need fill in the first 32 entries
    ;
    ; Before we fill it in we are going to invalidate everything in it
    ;
    mov edi, offset videopagetable
    mov ecx,1024
    mov eax, 0fffffffeh
    rep stosd
    ;
    mov edi, offset videopagetable
    mov eax,0c0000h-4096+7 ; Top page of the address to access
    mov ecx,32
fillvideo:
    mov [edi+ecx*4-4],eax  ;Fill in the table entry
    sub eax,4096           ; Move to offset of next lower page
    loop fillvideo         ; continue till done
    ;
    ; Now create the page directory.
    ;  The first 4 entries will refer to the 4 pages which hold our
    ;  16MB see-through paging.
    ;  The 512th entry will refer to the page which relocates our video
    ; to 80000000h
    ;
    ; Before we fill it in we are going to invalidate everything in it
    ;
    mov edi, offset PageDir
    mov ecx,1024
    mov eax, 0fffffffeh
    rep stosd
    ;
    mov eax,offset PageTables
    or al,7		; Add in the page table mode bits
    mov edi,offset pagedir
    mov [edi],eax
    add eax,4096
    mov [edi+4],eax
    add eax,4096
    mov [edi+8],eax
    add eax,4096
    mov [edi+12],eax
    mov eax,offset videopagetable
    or al,7		; Add in the page table mode bits
    mov [edi+512*4],eax
    pop es
    pop ds
    ret
    
initpagetables endp
;
; This ignores the BIOS data area, so our DOS clock will lose a
; couple of seconds
;
timerinterrupt proc
    push ds
    push eax
    push DS386
    pop ds	; Make sure DS is the data segment
    inc [timer] ; Increment the timer
    mov al,20h
    out 20h,al
    pop eax
    pop ds
    iretd
timerinterrupt endp
;
; Routine hooks timer interrupt, int 8.
;
; Since the hardware interrupts collide with 386 protected-mode build in
; traps, I am wont to reprogram the PICs to move the hardware interrupts
; elsewhere while in protected mode, but that's another topic
;
initinterrupt proc
    mov edi, 8 * 8 + offset tIDT; interrupts are 8 bytes long, 
				; we are hooking int 8
    mov eax,offset timerinterrupt
    mov [edi],ax	; Start by plopping in the code offset
    shr eax,16          ;
    mov [edi + 6],ax    ; High 16 bits
    mov word ptr [edi + 2],CS386 ; Now plop in the Code segment selector
    mov byte ptr [edi + 4],0 ; Always 0 for interrupts
    mov byte ptr [edi + 5],08eh ; Signifies an interrupt gate, present selector
    ret
initinterrupt endp
;
; Protected mode user program.  At this point everything is initialized
; except for unhandled interrupts.  Display data on the screen then
; disable the keyboard while we wait around for a couple of seconds
;
userprog proc
    ; Sample program to fill the screen with '!'

    push    es          ; Load the screen up with '!'
    push    fs
    push    fs
    pop es                      ; Load ES with DSABS
    mov ax,0721h                ; Black&white '!'
;
; Uncomment this line to access at the proper address
;    mov edi,0b8000h             ; screen address
;
; The next line accesses screen memory at the 80000000h page
    mov edi,80000000h + 18000h  ; B8000 = a0000 + 18000
    mov ecx,80*24               ; 24x80 screen
    rep stosw
    pop fs
    pop es
;
; Enable interrupts and wait around a couple of seconds
;
; First, disable keyboard interrupt
; This way if a key comes in we won't crash
;
    in al,21h	; PIC interrupt control port
    or al,2	; Bit to disable keyboard interrupts
    out 21h,al	; Do disable
    sti
tloop:
    cmp [timer],36 ; Wait until timer has ticked 36 times ( two seconds)
    jc tloop
    cli
;
; reenable keyboard interrupt
;
    in al,21h	; Read PIC interrupt port
    and al,0fdh ; reenable keyboard
    out 21h,al  ; Do enable
    ret
;
userprog endp
seg386 ends

    end bootstrap