/* kernel.c - the C part of the kernel */
/* Copyright (C) 1999  Free Software Foundation, Inc.
     
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
     
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
     
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
     
extern "C" {
#include <multiboot.h>
void cmain (unsigned long magic, unsigned long addr);
}

#include "kernel.h"
#include "screen.h"
#include "gdt.h"
#include "idt.h"
#include "intmgr.h"
#include "keyboard.h"
#include "memmgr.h"
#include "timer.h"
#include "task.h"

const char Kernel::version[] = "iCe ver.015";

/* Variables. */
Gdt    Gdt;
Idt    Idt;
KeyboardInterruptHandler KeyIntHandler;
Task   IdleTask((uint8*)stack, STACK_SIZE);

MsgQueue Kernel::message;

/* For Timer Test */
class JobTest : public TimerJob
{
private:
    Timer& timer;

public:
    JobTest(Timer& timer_, uint32 dur) : TimerJob(dur), timer(timer_) {}
    void onExpire(void) {
        Console.printf("JobTest\n");
        timer.add(*this);
    }
};
     
class TestTask : public Task {
public:
    TestTask(size_t sz) : Task(sz) {}
    void run(void) {
        int i = 0;
        Console.printf("Task 3 run ! \n");
        while(1) {
            asm volatile ("hlt");
            ++i;
            if (i % 100 == 0) Console.printf("3");
        }
    }
} task3(0x400);

/* Forward declarations. */
void itoa (char *buf, int base, int d);
     
void cmain (unsigned long magic, unsigned long addr)
{
    Kernel::initGlobalObjects();

    Console.clear();
    Console.printf("%s\n", Kernel::version);
     
    /* Am I booted by a Multiboot-compliant boot loader? */
    if (magic != MULTIBOOT_BOOTLOADER_MAGIC) {
        Console.printf ("Invalid magic number: 0x%x\n", (unsigned) magic);
        return;
    }

    /* Set MBI to the address of the Multiboot information structure. */
    multiboot_info_t* mbi = (multiboot_info_t*)addr;

    /* Are mem_* valid? */
    if ((mbi->flags & MBI_FLAG_MEM_INFO_VALID) != 0) {
      Console.printf ("mem_lower = %uKB, mem_upper = %uKB\n",
              (unsigned) mbi->mem_lower, (unsigned) mbi->mem_upper);
    }
     
    /* Bits 4 and 5 are mutually exclusive! */
    if ((mbi->flags & MBI_FLAG_AOUT_SYM_VALID) != 0 && 
        (mbi->flags & MBI_FLAG_ELF_SEC_VALID) != 0) {
        Console.printf ("Both bits 4 and 5 are set.\n");
        return;
    }

    MemoryManager.init(mbi);  // Enalbe alloc/free from here !!

    Gdt.init();
    Idt.init();

    InterruptManager.init();  // Enable interrupts from here !!

    InterruptManager.connect(IntMgr::KEYBOARD_IRQ, KeyIntHandler);
    InterruptManager.clearMask(IntMgr::KEYBOARD_MASK);

    SystemScheduler.init(SystemTimer, IdleTask);
    SystemScheduler.add(task3);

    SystemTimer.init();       // Setup timer interrupt

    TimerJob* job = new JobTest(SystemTimer, 500);
    SystemTimer.add(*job);

    KeyMapper mapper;
    while (true) {
        uint8 msg = Kernel::waitForMsg();
        KeyInfo info = mapper.translate(msg);
        //Console.printf("KEY MSG[%x]\n", info.AsciiCode);
        if ((info.AsciiCode != 0) && 
            (info.ExtendCode != KeyMapper::BREAK_KEY_CODE)) 
            Console.putchar(info.AsciiCode);
    }
}
     
/* Convert the integer D to a string and save the string in BUF. If
   BASE is equal to 'd', interpret that D is decimal, and if BASE is
   equal to 'x', interpret that D is hexadecimal. */
void itoa (char *buf, int base, int d) {
  char *p = buf;
  char *p1, *p2;
  unsigned long ud = d;
  int divisor = 10;
     
  /* If %d is specified and D is minus, put `-' in the head. */
  if (base == 'd' && d < 0) {
      *p++ = '-';
      buf++;
      ud = -d;
  } else if (base == 'x')
    divisor = 16;
     
  /* Divide UD by DIVISOR until UD == 0. */
  do {
      int remainder = ud % divisor;
      *p++ = (remainder < 10) ? remainder + '0' : remainder + 'a' - 10;
  } while (ud /= divisor);
     
  /* Terminate BUF. */
  *p = 0;
     
  /* Reverse BUF. */
  p1 = buf;
  p2 = p - 1;
  while (p1 < p2) {
      char tmp = *p1;
      *p1 = *p2;
      *p2 = tmp;
      p1++;
      p2--;
  }
}
     
// Global constructor/destructor
void Kernel::initGlobalObjects(void)
{
    extern const func_t __CTOR_LIST__[1];

    int32 i = (int32)__CTOR_LIST__[0];
    const func_t *p;

    if (i == -1) {
        for (i = 1; __CTOR_LIST__[i] != 0; i++);
        i--;
    }
    p = __CTOR_LIST__ + i;
    while (i--) (**p--)();
}

// for "halt message" in boot.S 
extern "C" void printf(const char* format, ...) {
    Console.printf(format);
}

extern "C" void __cxa_pure_virtual(void) {
    Kernel::panic("Call pure virtual function\n");
}
