/*
    exDOS - Experimental 32 Bit protected mode scripted operating system
    (C) 2005 Jason Hunt
    
    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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

    Contact: nulluser@gmail.com

    File: interrupt.c
*/

#include "types.h"
#include "stdio.h"
#include "interrupt.h"
#include "system.h"
#include "video.h"
#include "floppy.h"
#include "process.h"

#define CLOCK_TICK_RATE 1193180 // Freq of timer Xtal
 

volatile dword debug_count = 0;
volatile dword timer_ticks = 0;
volatile word timer_delay_count;


idt_entry idt[256];                     // Interrupt descriptor table


/* Init the programiable interval timer */
void setup_timer(word freq)
{
    word latch = ((CLOCK_TICK_RATE + (freq>>1)) / freq);

    out_byte(0x43, 0x34);//  mode 2, ch 0
 
    out_byte(0x40, latch & 0xff); // LSB
    out_byte(0x40, latch >> 8); // MSB
}
/* End of setup_timer */


/* TImer based delay function */
// The delay time is in units of 1/100 seconds
void timer_delay(word delay)
{
    if (delay == 0) return;

    timer_delay_count = delay;
    
    while (timer_delay_count);
}
/* End of timer_delay */


/* Unmask NMI */
void enable_nmi( void )
{
    out_byte(intr_data_1, 0);
    out_byte(intr_data_2, 0);
}
/* End of enable_nmi */


/* Mask NMI */
void disable_nmi( void )
{
    out_byte(intr_data_1, 0xff);        // Mask all all interrupts
    out_byte(intr_data_2, 0xff);
}
/* End of disable_nmi */


/* Timer interrupt */
void timer_intr( void )
{    
    timer_delay_count--;         // reduce the timer counter for delay

    timer_ticks++;

    if (!(timer_ticks % 100))
    {
        debug_count++;
        vga_draw_status();       
    }

    floppy_timer();            // chain to floppy timer, this needs to be a hook
}
/* End of timer_intr */


/* Modify an entry in the interrupt descriptor table */
void intr_set_vector(byte intr_num, void (*interrupt_func)())
{
    idt[intr_num].offset_low  = (dword)interrupt_func & 0xffff; // Linear addresws of 
    idt[intr_num].offset_high = (dword)interrupt_func >> 16;    // Interrupt
    idt[intr_num].selector = 0x08;                              // Code selector
    idt[intr_num].count = 0;                                    // Zero
    idt[intr_num].flags = 0x8e;                                 // Interrupt gate
}
/* End of set_idt_entry */


/* Init the interrupt system */
void interrupt_init(void)
{
    word i;

    for (i = 0; i < 256; i++)                   // Create empty exceptions
        intr_set_vector(i, empty_exception);

    for (i = 0x20; i < 0x28; i++)               // Create empty hardware interrupts
        intr_set_vector(i, empty_intr_0_7);

    for (i = 0x28; i <= 0x2F; i++)              // Create empty hardware interrupts
        intr_set_vector(i, empty_intr_8_F);

        
    intr_set_vector(0x20, timer_intr_wrap);       // (Install the timer interrupt
    intr_set_vector(0x21, keyboard_intr_wrap);    // Install the keyboard interrupt

    intr_set_vector(0x30, syscall_intr_wrap);    // Install the syscall interface
        
    word idt_base[3];                           // This will become the IDT struct

    idt_base[0] =  256*8 - 1;                   // Limit 
    idt_base[1] =  (dword)&idt & 0xffff;        // Pointer low
    idt_base[2] =  (dword)&idt >> 16;           // Pointer high

    load_idt((void *)&idt_base);                // Load the idt
   
    setup_timer(100);                           // Reprogram the PIT (100 hz)

    enable_nmi();                               // Re-enable nmi
    
    sti();                                      // Enable interrupts
}
/* End of interrupt_init */







