#include "stdint.h"
#include "Arch/x86/custom_defs.h"

uint32_t readl(uint32_t addr)
{
    uint32_t* ptr = (uint32_t*)addr;
    return *ptr;
}

void writel(uint32_t addr, uint32_t val)
{
    uint32_t* ptr = (uint32_t*)addr;
    *ptr = val;
}

void delay(uint32_t delay)
{
    float k=0;
    for(uint32_t i=0;i<delay;i++)
    {
      for(uint32_t j=0;j<delay;j++)
      {
        if(k)
          k=0;
        else k=1;
      }
    }
}

void outb(uint16_t port, uint8_t val)
{
    asm volatile ( "outb %0, %1" : : "a"(val), "Nd"(port) );
}

uint8_t inb(uint16_t port)
{
    uint8_t ret;
    asm volatile ( "inb %1, %0" : "=a"(ret) : "Nd"(port) );
    return ret;
}

void outw(uint16_t port, uint16_t val)
{
    asm volatile ( "outw %0, %1" : : "a"(val), "Nd"(port) );
}

uint16_t inw(uint16_t port)
{
    uint16_t ret;
    asm volatile ( "inw %1, %0" : "=a"(ret) : "Nd"(port) );
    return ret;
}
/*
void outpd(uint16_t port, uint16_t val)
{
    asm volatile ( "outpd %0, %1" : : "a"(val), "Nd"(port) );
}

uint16_t inpd(uint16_t port)
{
    uint16_t ret;
    asm volatile ( "inpd %1, %0" : "=a"(ret) : "Nd"(port) );
    return ret;
}
*/

void outl(uint16_t port, uint32_t val)
{
    asm volatile ( "outl %0, %1" : : "a"(val), "Nd"(port) );
}

uint32_t inl(uint16_t port)
{
    uint32_t ret;
    asm volatile ( "inl %1, %0" : "=a"(ret) : "Nd"(port) );
    /* TODO: Is it wrong to use 'N' for the port? It's not a 8-bit constant. */
    /* TODO: Should %1 be %w1? */
    return ret;
}

void io_wait(void)
{
    /* Port 0x80 is used for 'checkpoints' during POST. */
    /* The Linux kernel seems to think it is free for use :-/ */
    asm volatile ( "outb %%al, $0x80" : : "a"(0) );
    /* TODO: Is there any reason why al is forced? */
}

bool are_interrupts_enabled()
{
    unsigned long flags;
    asm volatile ( "pushf\n\t"
                   "pop %0"
                   : "=g"(flags) );
    return flags & (1 << 9);
}

void lidt_n(void* base, uint16_t size)
{   // This function works in 32 and 64bit mode
    struct {
        uint16_t length;
        void*    base;
    } __attribute__((packed)) IDTR = { size, base };

    asm ( "lidt %0" : : "m"(IDTR) );  // let the compiler choose an addressing mode
}

void sysManager(unsigned int todo) // 1 : reboot; 2 : shutdown; 3 : reserved
{
    if(todo==1)
        outb(0x64, 0xFE);
    else if(todo==2)
        //abort();    //acpiPowerOff();
        return;
    else if(todo==3)
        return;
}

void
stosb(void *addr, int data, int cnt)
{
  asm volatile("cld; rep stosb" :
               "=D" (addr), "=c" (cnt) :
               "0" (addr), "1" (cnt), "a" (data) :
               "memory", "cc");
}

void
stosl(void *addr, int data, int cnt)
{
  asm volatile("cld; rep stosl" :
               "=D" (addr), "=c" (cnt) :
               "0" (addr), "1" (cnt), "a" (data) :
               "memory", "cc");
}

void
ltr(uint16_t sel)
{
  asm volatile("ltr %0" : : "r" (sel));
}

uint32_t
readeflags(void)
{
  uint32_t eflags;
  asm volatile("pushfl; popl %0" : "=r" (eflags));
  return eflags;
}

void
loadgs(uint16_t v)
{
  asm volatile("movw %0, %%gs" : : "r" (v));
}

uint32_t
xchg(volatile uint32_t *addr, uint32_t newval)
{
  uint32_t result;

  // The + in "+m" denotes a read-modify-write operand.
  asm volatile("lock; xchgl %0, %1" :
               "+m" (*addr), "=a" (result) :
               "1" (newval) :
               "cc");
  return result;
}

uint32_t
rcr2(void)
{
  uint32_t val;
  asm volatile("movl %%cr2,%0" : "=r" (val));
  return val;
}

void
lcr3(uint32_t val)
{
  asm volatile("movl %0,%%cr3" : : "r" (val));
}

inline void cli()
{
	asm volatile("cli");
}

inline void sti()
{
	asm volatile("sti");
}

inline void sti_int50()
{
    asm volatile("sti;\
		int $51");
}

