{ 
	Global Descriptor Table - Data Structures & Interface.
	
	@author(Kieron Morris <kjm@kieronmorris.me>)
}
unit gdt;

interface

uses
    console;

type
    TGDT_Entry = packed record
        limit_low   : uint16;
        base_low    : uint16;
        base_middle : uint8;
        access      : uint8;
        granularity : uint8;
        base_high   : uint8;
    end;
    PGDT_Entry = ^TGDT_Entry;

    TGDT_Pointer = packed record
        limit : uint16;
        base  : uint32;
    end;

var
    gdt_entries : array[0..1023] of TGDT_Entry;
    gdt_pointer : TGDT_Pointer;

procedure init();
procedure set_gate(Gate_Number : uint32; Base : uint32; Limit : uint32; Access : uint8; Granularity : uint8);
procedure flush;
procedure reload;

implementation

procedure flush_gdt(gdt_pointer : uint32); assembler; nostackframe;
asm
    MOV EAX, gdt_pointer
    LGDT [EAX]
    MOV AX, $10
    MOV DS, AX
    MOV ES, AX
    MOV FS, AX
    MOV GS, AX
    MOV SS, AX
    db $EA          // Bypass stupid inline ASM restrictions-
    dw @@flush, $08 // by assembling the farjump instruction ourselves.                    
@@flush:            // It's just data, honest gov'.
end;

procedure flush;
begin
    console.outputln('GDT','FLUSH.');
    flush_gdt(uint32(@gdt_pointer));
end;

procedure reload_gdt(gdt_ptr : uint32); assembler;
asm
    MOV EAX, gdt_ptr
    LGDT [EAX]
end;

procedure reload;
begin
    console.outputln('GDT','RELOAD.');
    reload_gdt(uint32(@gdt_pointer));
end;

procedure set_gate(Gate_Number : uint32; Base : uint32; Limit : uint32; Access : uint8; Granularity : uint8);
var
    lLimit : uint32;

begin
    lLimit:= (Gate_Number + 1) * 8;
    lLimit:= lLimit - 1;
    if lLimit > gdt_pointer.limit then begin
        gdt_pointer.limit:= lLimit;
    end;
    gdt_entries[Gate_Number].base_low    := (Base AND $FFFF);
    gdt_entries[Gate_Number].base_middle := (Base SHR 16) AND $FF;
    gdt_entries[Gate_Number].base_high   := (Base SHR 24) AND $FF;
    gdt_entries[Gate_Number].limit_low   := (Limit AND $FFFF);
    gdt_entries[Gate_Number].granularity := ((Limit SHR 16) AND $0F) OR (Granularity AND $F0);
    gdt_entries[Gate_Number].access      := Access;    
    console.output('GDT','GATE ');
    console.writeint(Gate_Number);
    console.writestringln(' SET.');
end;

procedure init();
begin
    console.outputln('GDT','INIT START.');
    gdt_pointer.limit:= 0;
    gdt_pointer.base  := uint32(@gdt_entries);  
    set_gate($00, $00, $00,       $00, $00); //OFFSET: 0
    set_gate($01, $00, $FFFFFFFF, $9A, $CF); //OFFSET: 8
    set_gate($02, $00, $FFFFFFFF, $92, $CF); //OFFSET: 16
    set_gate($03, $00, $FFFFFFFF, $FA, $CF); //OFFSET: 24
    set_gate($04, $00, $FFFFFFFF, $F2, $CF); //OFFSET: 32
    flush;
    console.outputln('GDT','INIT END.');
end;

end.