#include "gdt.h"

unsigned short create_selector(unsigned short index, unsigned char type,
        unsigned char rpl)
{
  unsigned short ret = 0;
  ret += index << 3;
  ret += type & 0x0004;
  ret += rpl & 0x0003;
  return ret;
}

struct gdt_entry create_gdt_entry(unsigned int start, unsigned short n)
{
  struct gdt_entry ret;
  ret.size = ((n * 8) - 1) & 0xFFFF;
  ret.address = start;
  return ret;
}

struct segment_descriptor create_descriptor(unsigned int base, unsigned int
        limit, unsigned char dpl, unsigned char segment_type)
{
  /*
  unsigned short base_1 = (base & 0x0000FFFF) << 4;
  unsigned char base_2 = (base & 0x00FF0000) << 2;
  unsigned char base_3 = (base & 0xFF000000);
  unsigned short limit_1 = (limit & 0x0000FFFF) << 4;
  */

  struct segment_descriptor ret;
  ret.first_bytes = (base & 0x0000FFFF) + ((limit & 0x0000FFFF) << 16);
  ret.last_bytes = ((base & 0x00FF0000) >> 16) + (((segment_type & 0x0F) +
        ((dpl & 0x03) << 5) + 0x90) << 8 ) + (limit & 0x000F0000) +
        (base & 0xFF000000);

  return ret;
}

void add_in_gdt(unsigned int i, unsigned int start, struct segment_descriptor
        desc)
{
  int index = i * 8;
  struct segment_descriptor *ptr = (struct segment_descriptor *) start;

  *(ptr + index) = desc;
}

void config_gdt(void)
{
  /* int base = 0x00000010; */
  int base = 0x00008f60;
  struct gdt_entry entry = create_gdt_entry(base, 10);
  /* unsigned short data_selector = create_selector(2, 0, 0); */
  struct segment_descriptor null = {0, 0};
  struct segment_descriptor code_kernel = create_descriptor(0, 0xFFFFFFFF, 0, 0x0A);
  struct segment_descriptor data_kernel = create_descriptor(0, 0xFFFFFFFF, 0, 0x02);

  load_cs();
  load_registers();
  add_in_gdt(0, base, null);
  add_in_gdt(1, base, code_kernel);
  add_in_gdt(2, base, data_kernel);
  load_gdt(&entry);
}