+ push kbintr
+ push dword IRQ_TO_INTR(KB_IRQ)
+ call set_intr
+ add esp, 8
+
+ ; enable keyboard interrupt
+ mov eax, KB_CMD_GET_CMDBYTE
+ call send_cmd
+ call read_data
+ or eax, KB_CCB_KB_INTREN ; set the INTREN flag
+ push eax
+ mov eax, KB_CMD_SET_CMDBYTE
+ call send_cmd
+ pop eax
+ call send_data
+
+ ; flush the read buffer
+ call wait_read
+ jz .skipread
+ call read_data
+.skipread:
+ ret
+
+ ; keyboard interrupt handler
+kbintr:
+ pusha
+ in al, KB_DATA_PORT
+ cmp al, 0e0h
+ jnz .noext
+ mov byte [key_ext], 1
+ jmp .eoi
+.noext:
+ ; keystate[keycode] address in ebx
+ xor ebx, ebx
+ mov bl, al
+ add ebx, keystate
+
+ ; determine if it's a press or release
+ test al, 80h
+ jz .press
+ ; key release
+ mov byte [ebx], 0
+ jmp .eoi
+.press: ; key press
+ mov byte [ebx], 1
+
+.eoi:
+ cli
+ end_of_irq KB_IRQ
+
+ popa
+ iret
+
+%macro iodelay 0
+ xor al, al
+ times 7 out 80h, al
+%endmacro
+
+wait_write:
+ push eax
+ push ecx
+ mov ecx, 32768
+.loop: in al, KB_STATUS_PORT
+ test al, KB_STAT_INBUF_FULL
+ jz .break
+ iodelay
+ dec ecx
+ jnz .loop
+.break: pop ecx
+ pop eax
+ ret
+
+wait_read:
+ push eax
+ push ecx
+ mov ecx, 32768
+.loop: in al, KB_STATUS_PORT
+ test al, KB_STAT_OUTBUF_FULL
+ jnz .break
+ iodelay
+ dec ecx
+ jnz .loop
+.break: pop ecx
+ pop eax
+ ret
+
+ ; expects command in al
+send_cmd:
+ call wait_write
+ out KB_CMD_PORT, al
+ ret
+
+read_data:
+send_data:
+
+ section .data
+ align 4
+key_ext: db 0
+
+ global keystate
+keystate: times 256 db 0