'******************************************************************************
'* 
'* KEYBOARD CONTROLLER i8048 (CHIP DEVICE)
'*
'* Was used in 83-key keyboards of PC/XT-class machines
'*
'* Supported platform/bus: X86
'* 
'* Version history:
'*  2007,2008 - initial emulation (by WadiM)
'*
'****************************************************************************** 

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

//ISA device interface support
public use object DEVICE_ISA

//device name
DeviceName="KBC i8048"
DebugName="KBC_I8048"

//Modules
protected use module "x86keys\scancode" 'scancode conversion tables

//Constants
const KEY_DELAY_FREQ as word=2   'deley before key autorepeat (1/2=0.5 sec)
const KEY_REPEAT_FREQ as word=20 'key autorepeat time (1/10=0.05 sec)
const MAX_BUFFER_SIZE as word=64 'in reality it is 16, but bigger is better :)

//Keyboard buffer
use object FIFO_STREAM as buffer

//Matrix of virtual key states
dim vkey_state(256) as boolean 'pressed/released currently
dim last_vkey_pressed as byte=VK_NONE 'to autorepeat it

//Variables
dim irq_count as word=0 'count of unhandled IRQs
dim eventid_repeat as dword=0 'event identifier of autorepeat key events
dim autorepeat_pending as boolean=false 'autorepeat need to be activated
dim i as integer, b as byte


'-------------------------------- H/W Interface -------------------------------

//Write data port
public property DATA_PORT(Value as byte)
 ?? DebugPrefix;">DataPort=";Hex(Value,2);"h"
 //TODO
end

//Read data port (scan codes not removed from buffer)
public function DATA_PORT as byte
 if buffer.size>0 then result=buffer.PopByte(false) else result=0xFF 'empty
 ?? DebugPrefix;"<DataPort=";Hex(result,2);"h"
end

//Inform controller that key was processed (and remove scancode from buffer)
public procedure KeyProcessed
 if buffer.size>0 then 'buffer was not empty
    buffer.PopByte : end if
 ?? DebugPrefix;">KeyProcessed=true"
end

//Handle key from emulator (convert to scancode and place to buffer)
public procedure HandleKey(vkey as byte, pressed as boolean)
 ?? DebugPrefix;">VKey=";Hex(vkey,2);"h (";iif(pressed,"DOWN","UP");")";
 dim can_autorepeat as boolean=last_vkey_pressed<>vkey
 last_vkey_pressed=VK_NONE : autorepeat_pending=false 
 dim scode as byte=iif(pressed,XTKeyDown(vkey,0),XTKeyUp(vkey,0))
 dim scode_size as byte=iif(pressed,XTKeyDown(vkey,1),XTKeyUp(vkey,1))
 if scode_size=1 then //known key/scancode
   if pressed then 'key pressed
      last_vkey_pressed=vkey 
      if can_autorepeat then 'init autorepeat event
         ?? " NEED AUTOREPEAT ";
         autorepeat_pending=true
         eventid_repeat=eventid_repeat+1 'to discard old events
      end if
   end if
   if buffer.Size>=MAX_BUFFER_SIZE then //buffer overflow
      //TODO - noisy beep
   else //buffer has space
      buffer.PushByte(scode) 'add to buffer
     ?? " (XTKey=";Hex(scode,2);"h)";
     if DEVPARAM_IRQ>=0 then 'raise IRQ
        if not(pc.SetIRQ(DEVPARAM_IRQ,true)) then irq_count=irq_count+1
   end if : end if

// else : ?? "scode=";Hex(scode,2);" scode_size=";scode_size
 end if
 ?? 'finish printed string
end

//Autorepeat event (initiated by delay event)
function RepeatEvent(freq as integer,eventid as dword) as boolean
 if (last_vkey_pressed<>VK_NONE) and (eventid=eventid_repeat) then //make event
   ?? "REPEAT EVENT"
   HandleKey(last_vkey_pressed,true) 'emulate virtual key pressing
   result=true
 else
   ?? "REPEAT EVENT STOP"
  result=false 'no need next event
 end
end

//Autorepeat delay event (initiated by irq processing event)
function DelayEvent(freq as integer,eventid as dword) as boolean
 result=false 'no need next event
 ?? "DELAY EVENT"
 if (last_vkey_pressed<>VK_NONE) and (eventid=eventid_repeat) then //make event
   ?? DebugPrefix;"REPEAT REGISTERED"
   pc.NewFreqEvent(KEY_REPEAT_FREQ,eventid)=RepeatEvent //register event
 end if
end

//Unhandled IRQ's processing event
function IRQEvent(freq as integer,eventid as dword) as boolean
 result=true 'need next event
 if autorepeat_pending then 'activate autorepeat delay event
    autorepeat_pending=false
    eventid_repeat=eventid_repeat+1 'to discard old events (safer to duplicate)
    pc.NewFreqEvent(KEY_DELAY_FREQ,eventid_repeat)=DelayEvent //register event
    ?? DebugPrefix;"DELAY REGISTERED"
 end
 if (irq_count<>0) and (DEVPARAM_IRQ>=0) then 
 dim i as integer : for i=0 to irq_count-1 
 if pc.SetIRQ(DEVPARAM_IRQ,true) then irq_count=irq_count-1 else exit for
 next : end if
end

'---------------------------- DEVICE Interface --------------------------------

//Device initialization
public function DEV_INIT(stream as object,byref EventFreq as integer) as boolean

 'parent call
 if not DEV_INIT(stream,EventFreq) then exit(false)
 if EventFreq<100 then EventFreq=100

 //register events
 pc.NewFreqEvent(50)=IRQEvent '50HZ - sufficient for keyboard

 //success
 result=true

end 

       
