'*****************************************************************************
'* INT 09H (BIOS) HARDWARE KEYBOARD INTERRUPT (IRQ1)
'* Params: -
'* Result: AH - scancode
'*         AL - ASCII-code
'*****************************************************************************

//DEBUG.ON 'uncomment to enable debug messages (can be slow for hi-freq events)

//Modules
use module "tools" 'BIOS tools

//Variables
dim i as integer, k as integer, n as integer, skeys as word, scancode as byte
dim b as byte, pressed as boolean, w as word, keycode as word

//Extended key params (scancodes and their mask)
dim ecode as dword=0, emask as dword=0

'--------------------------- Interrupt handler --------------------------------

//Interrupt handler
public function EINT_09H as boolean : result=false

//read scancode from controller (only if CF flag not set, else from AL)
if cpu.FLAGS.CF=true then scancode=cpu.AL else scancode=pc.ReadPort(0x60) 

//inform XT keyboard controller, that key processed (no need in AT case)
b=pc.ReadPort(0x61) : pc.WritePort(0x61,b or 0x80) : pc.WritePort(0x61,b)

?? "[INT 09h] Scancode=";Hex(scancode,2);"h"

//detect scancode E0h (this is prefix for extended AT-keys) 
if scancode=0xE0 then : ecode=0xE0 : emask=0xFF 
   ?? "[INT 09h] ExtCode=";Hex(scancode,2);"h" : result=true : exit : end

//get key state (pressed or released) and clear flag from scancode
pressed=(scancode and 0x80)=0 : scancode=scancode and (not 0x80)

//read switching key flags from BIOS data area
skeys=mem.Word(0x417)

//analize kind of scancode (switching key or ascii-key) and process accordingly
dim swkey_flag as boolean=false 'flag of switching key processing
select case scancode
case 0x1D //CTRL
  if pressed then skeys=skeys or 0x4 else skeys=skeys and (not 0x4)
  if (ecode and emask)=0xE0 then : skeys=skeys or 0x10 : ?? "[INT 09h] RCTRL"
     else : skeys=skeys and (not 0x10) : ?? "[INT 09h] LCTRL" : end if
     swkey_flag=true
case 0x38 //ALT
  if pressed then skeys=skeys or 0x8 else skeys=skeys and (not 0x8)
  if (ecode and emask)=0xE0 then : skeys=skeys or 0x20 : ?? "[INT 09h] RALT"
     else : skeys=skeys and (not 0x20) : ?? "[INT 09h] LALT" : end if
     swkey_flag=true
case 0x2A //LSHIFT
  if pressed then skeys=skeys or 0x2 else skeys=skeys and (not 0x2)
  ?? "[INT 09h] LSHIFT" : swkey_flag=true
case 0x36 //RSHIFT
  if pressed then skeys=skeys or 0x1 else skeys=skeys and (not 0x1)
  ?? "[INT 09h] RSHIFT" : swkey_flag=true
case 0x3A //CAPS LOCK
  if pressed then : skeys=skeys or 0x4000 'state flag only (without mode flag)
   else 'mode changed on release (temporary measure to ignore autorepeats)
    skeys=skeys and (not 0x4000) 'state flag
    if (skeys and 0x40)=0 then skeys=skeys or 0x40 else skeys=skeys and (not 0x40)
   end if
   ?? "[INT 09h] CAPSLOCK" : swkey_flag=true
case 0x45 //NUM LOCK
  if pressed then : skeys=skeys or 0x2000 'state flag only (without mode flag)
   else 'mode changed on release (temporary measure to ignore autorepeats)
    skeys=skeys and (not 0x2000) 'state flag
    if (skeys and 0x20)=0 then skeys=skeys or 0x20 else skeys=skeys and (not 0x20)
   end if
   ?? "[INT 09h] NUMLOCK" : swkey_flag=true
case 0x46 //SCROLL LOCK
  if pressed then : skeys=skeys or 0x1000 'state flag only (without mode flag)
   else 'mode changed on release (temporary measure to ignore autorepeats)
    skeys=skeys and (not 0x1000) 'state flag
    if (skeys and 0x10)=0 then skeys=skeys or 0x10 else skeys=skeys and (not 0x10)
   end if
   ?? "[INT 09h] SCROLLLOCK" : swkey_flag=true
case else 'ordinary (ascii-related) key processing
  select case scancode //some of them used to toggle modes also
  case 0x52 //INSERT
   if pressed then : skeys=skeys or 0x8000 'state flag only (without mode flag)
   else 'mode changed on release (temporary measure to ignore autorepeats)
     skeys=skeys and (not 0x8000) 'state flag
     if (skeys and 0x80)=0 then skeys=skeys or 0x80 else skeys=skeys and (not 0x80)
   end if
   ?? "[INT 09h] INSERT MODE" : mem.Word(0x417)=skeys
  case 0x54 //PAUSE
   if pressed then 'state flag absent (without mode flag)
   else 'mode changed on release (temporary measure to ignore autorepeats)
     if (skeys and 0x800)=0 then skeys=skeys or 0x800 else skeys=skeys and (not 0x800)
   end if
   ?? "[INT 09h] PAUSE MODE" : mem.Word(0x417)=skeys
  end select
  //convert to ascii and place to buffer
  if not pressed then : result=true : exit : end 'if released, no need it more
  ?? "[INT 09h] ASCII-keycode=";Hex(keycode,2);"h"
  if not(ConvertXTScanCode(scancode,skeys,ecode and emask,keycode)) then 
     ?? "???" : exit : else ?? Hex(keycode,2);"h" : end if
end select

//clear extended keys (by clearing mask)
emask=0

//if switching flags changed (it is switching key), write it to data area
if swkey_flag then
   mem.Word(0x417)=skeys
elseif pressed then //if key was pressed, then write to BIOS buffer
   dim head as word, tail as word, newtail as word
   head=mem.Word(0x41A) 'read buffer head pointer
   tail=mem.Word(0x41C) 'read buffer tail pointer
   newtail=tail+2 : if newtail>=0x43D then newtail=0x41E 'calc new tail
   if newtail=head then exit(true) 'buffer full //TODO Beep
   mem.Word(tail)=keycode 'write keycode to buffer at current tail
   mem.Word(0x41C)=newtail 'store new buffer tail pointer
end if 'if ordinary key released, needn't to do anything


//dbg.break
//success
result=true : end
