#include "gdt.h"

void Gdt::init(void) {
    table.limit  = Gdt::NUM * sizeof(GdtEntry) - 1;
    table.offset = (uint32)entry;
    for (int i = 0; i < Gdt::NUM; i++) {
        set(i, 0, 0, 0);
    }

    set(NULL_S, 0, 0, 0);                       // for Null seg
    set(KERNEL_CS, 0, 0xFFFFF, KERNEL_CS_FLAG); // for Kernel code 
    set(KERNEL_DS, 0, 0xFFFFF, KERNEL_DS_FLAG); // for Kernel data 

    load();

    /* Change segments */
    asm("ljmp  %0, $1f; 1:" : : "i" (KERNEL_CS * sizeof(GdtEntry)));
    asm("mov  %0, %%ax" : : "i" (KERNEL_DS * sizeof(GdtEntry)));
    asm("mov  %ax, %ds");
    asm("mov  %ax, %es");
    asm("mov  %ax, %fs");
    asm("mov  %ax, %gs");
    asm("mov  %ax, %ss");
}

void Gdt::set(int index, uint32 segbase, uint32 limit, uint16 type) {
    GdtEntry& desc = entry[index];

    desc.limit_low   = limit & 0xFFFF;
    desc.limit_hi    = (limit >> 16) & 0xF;
    desc.segbase_low = segbase & 0xFFFF;
    desc.segbase_mid = (segbase >> 16) & 0xFF;
    desc.segbase_hi  = (segbase >> 24) & 0xFF;
    desc.type_low    = type & 0xFF;
    desc.type_hi     = (type >> 8) & 0xF;
}

void Gdt::load(void) const {
    __asm__ __volatile__("lgdt %0" :: "m" (table));
}
