@declare_func("__kget_internal_irq_table", "ptr", "()")@
@declare_func("__klidt", "void", "(i8*)")@
@declare_func("__kwritepb", "void", "(i8, i8)")@
@declare_func("__kreadpb", "ubyte", "(i8)")@
import mm


type InternalIDTHandlerLookupTableEntry is packed
int addr
endtype

type InternalIDTHandlerLookupTable is packed
int count

function(InternalIDTHandlerLookupTable t, int idx) InternalIDTHandlerLookupTable:get -> int does
return ((t|ptr):offset(4+(4*idx))|InternalIDTHandlerLookupTableEntry).addr
endtype

function(int p, int d) write_port -> void does
__kwritepb(p|byte, d|byte)
return

function(int p) read_port -> int does
return __kreadpb(p|byte)|int

type IRQHandler is a function->void
type IRQHandlerBox is packed
IRQHandler handler
endtype

type IRQHandlerTable is
function(int count) IRQHandlerTable::new -> IRQHandlerTable does
IRQHandlerTable new = malloc(4*count)|IRQHandlerTable
memset(new|ptr, 4*count, 0|byte)
return new

function(IRQHandlerTable t, int idx, IRQHandler handler) IRQHandlerTable:set -> void does
((t|ptr):offset(4*idx)|IRQHandlerBox).handler=handler
return

function(IRQHandlerTable t, int idx) IRQHandlerTable:get -> IRQHandler does
return ((t|ptr):offset(4*idx)|IRQHandlerBox).handler
endtype

type IDTEntry is packed
short offset_low
short selector
byte zero
byte type
short offset_high

function(IDTEntry entry, int irupt_addr) IDTEntry:set -> void does
entry.offset_low=(irupt_addr & 0xFFFF)|short
entry.selector=0x08|short
entry.zero=0|byte
entry.type=0x8E|byte
entry.offset_high=((irupt_addr&0xFFFF0000) >> 16)|short
return
endtype

type IDT is packed
short idt_size
int idt_address
IRQHandlerTable handlers

function() IDT::create -> IDT does
IDT new = malloc(@sizeof(IDT)@)|IDT
new.idt_size=(((@sizeof(IDTEntry)@)*256)-1)|short #Said -1
new.idt_address=malloc((new.idt_size|int)+1)|int
memset(new.idt_address|ptr, (new.idt_size|int)+1, 0|byte)
new.handlers=IRQHandlerTable::new(256)
return new

function(IDT table, int internal_handler, int idx) IDT:install_internal_entry -> void does
int entry_sz=@sizeof(IDTEntry)@
ptr target=(table.idt_address|ptr):offset(entry_sz*idx)
(target|IDTEntry):set(internal_handler)
return
endtype

type IRQRegs is packed
endtype

IDT idt

function() setup_irq -> void does
idt=IDT::create()

kterm:print("IDT@")
kterm:dumphexi(idt.idt_address)

InternalIDTHandlerLookupTable ihtbl = __kget_internal_irq_table()|InternalIDTHandlerLookupTable
int idx=0
while idx<ihtbl.count do
idt:install_internal_entry(ihtbl:get(idx), idx)
idx+=1
done

kterm:print("!")

idt.handlers:set(0x00, irq_zerodivision_handler|IRQHandler)
idt.handlers:set(0x08, irq_doublefault_handler|IRQHandler)
idt.handlers:set(0x0D, irq_protectionfault_handler|IRQHandler)
idt.handlers:set(0x0E, irq_pagefault_handler|IRQHandler)

idt.handlers:set(0x20, irq_timer_handler|IRQHandler)
idt.handlers:set(0x21, irq_keyboard_handler|IRQHandler)

#Mask interrupts we don't have handlers for
setup_kbd()
setup_pit()

kterm:print(".")

__klidt(idt|ptr)

kterm:print(".")

start_irqs()
kterm:print(" KBD, PIC started. ")
read_port(0x60)
return

function() setup_kbd -> void does
write_port(0x20 , 0x11)
write_port(0xA0 , 0x11)
write_port(0x21 , 0x20)
write_port(0xA1 , 0x28)
write_port(0x21 , 0x00)
write_port(0xA1 , 0x00)
write_port(0x21 , 0x01)
write_port(0xA1 , 0x01)
write_port(0x21 , 0xff)
write_port(0xA1 , 0xff)
return

function() setup_pit -> void does
write_port(0x43 , 0x36) #Square wave mode
write_port(0x40, 0) #65536 ticks/sec, 54.9 ms
write_port(0x40, 0) #65536 ticks/sec, 54.9 ms
return

type KeyboardEvent is
int scancode
bool state

function(int scancode, bool state) KeyboardEvent::new -> KeyboardEvent does
KeyboardEvent new = malloc(@sizeof(KeyboardEvent)@)|KeyboardEvent
new.scancode=scancode
new.state=state
return new
endtype

type KeyboardConsumerCallback is a function->void

type KeyboardState is
ptr button_state_map #bitmap of states (0=up, 1=down) for buttons (based on scancode)
KeyboardConsumerCallback handler

function() KeyboardState::new -> KeyboardState does
KeyboardState new = malloc(@sizeof(KeyboardState)@)|KeyboardState
new.button_state_map=malloc(128)
memset(new.button_state_map, 128, 0|byte)
new.handler=0|KeyboardConsumerCallback
return new

function(KeyboardState s, uint scancode) KeyboardState:getkey -> bool does
if scancode>127 do
ikpanici("Internal Error - Invalid Scancode in KeyboardState:getkey", scancode)
done
return s.button_state_map[scancode]!=0|byte

function(KeyboardState s, uint scancode, bool state) KeyboardState:setkey -> void does
if scancode>127 do
ikpanici("Internal Error - Invalid Scancode in KeyboardState:setkey", scancode)
done
s.button_state_map[scancode]=state|byte

if s.handler|int!=0 do
s.handler(scancode, state)
done
return

function(KeyboardState s, KeyboardConsumerCallback handler) KeyboardState:set_handler -> void does
s.handler=handler
return
endtype

KeyboardState system_kbdstate
long pic_ticks

function() start_irqs -> void does
system_kbdstate=KeyboardState::new()
pic_ticks=0|long
write_port(0x21 , 0b11111100)
# ^^- Enable IRQ0 (0x20 after translation) for PIT
# |- Enable IRQ1 (0x21 after translation) for keyboard
return

function(int cr2, int exception_id, int errorcode) kirq_callback -> void does
IRQHandler h = idt.handlers:get(exception_id)
if (h|ptr|int)==0 do
kpanic(errorcode, exception_id, "No handler for IRQ", "A unregistered IRQ was fired (unhandled IRQ)")
done
h(0|ptr|IRQRegs, errorcode, cr2)
write_port(0x20, 0x20)
return

function(IRQRegs regs, int errorcode, int cr2) irq_keyboard_handler -> void does
int status = read_port(0x64)
if (status&1) > 0 do
int value = read_port(0x60)
int keycode = value&0b01111111
system_kbdstate:setkey(keycode, (value&0b10000000)==0)
done
return

function(IRQRegs regs, int errorcode, int cr2) irq_pagefault_handler -> void does
kpanic(errorcode, cr2, "Unrecoverable Page Fault Error - INT 0x0E - #PF", "???")
return

function(IRQRegs regs, int errorcode, int cr2) irq_protectionfault_handler -> void does
kpanic(errorcode, cr2, "Unrecoverable Page Fault Error - INT 0x0D - #GPF", "???")
return

function(IRQRegs regs, int errorcode, int cr2) irq_doublefault_handler -> void does
kpanic(errorcode, cr2, "Unrecoverable Double Fault Error - INT 0x08 - #DF", "???")
return

function(IRQRegs regs, int errorcode, int cr2) irq_zerodivision_handler -> void does
kpanic(errorcode, cr2, "Unrecoverable Zero Division Error - INT 0x00 - #DBZ", "Why would you do that?")
return

function(IRQRegs regs, int errorcode, int cr2) irq_timer_handler -> void does
pic_ticks+=1|long
return