#include <EDK.h>
#include "General.h"
#include "CKeyboard.h"

#define Class CKeyboard
extern Class MSVC4Bug;

#define _TRACE 0

const byte bWait = (byte)-1;
const byte bNone = 80;

proc(SetKeyRow)
  mov AH,0xFF
  test AL,0x01
  jne NoBit0
  and AH,mvar(abMatrix[0])
NoBit0:
  test AL,0x02
  jne NoBit1
  and AH,mvar(abMatrix[1])
NoBit1:
  test AL,0x04
  jne NoBit2
  and AH,mvar(abMatrix[2])
NoBit2:
  test AL,0x08
  jne NoBit3
  and AH,mvar(abMatrix[3])
NoBit3:
  test AL,0x10
  jne NoBit4
  and AH,mvar(abMatrix[4])
NoBit4:
  test AL,0x20
  jne NoBit5
  and AH,mvar(abMatrix[5])
NoBit5:
  test AL,0x40
  jne NoBit6
  and AH,mvar(abMatrix[6])
NoBit6:
  test AL,0x80
  jne NoBit7
  and AH,mvar(abMatrix[7])
NoBit7:
  mov AL,AH
  SetPort(mvar(KeyCol), 1)
  ret
endp

void CKeyboard::KeyOnRowChange(byte bState, byte bChanges) {
  __asm {
  push EBX
  push ThisReg
  mov ThisReg,ECX
  mov AL,bState
  mov AH,bChanges

  call fn(SetKeyRow)

  pop ThisReg
  pop EBX
  }
}

void CKeyboard::KeyOnColChange(byte /*bState*/, byte /*bChanges*/) {
  // reverse keyboard matrix not implemented yet
}

char* gapcC64Keys[128] = {
  "Del", "Return", "Cursor Right", "F7", "F1", "F3", "F5", "Cursor Down",
  "3", "W", "A", "4", "Z", "S", "E", "Shift",
  "5", "R", "D", "6", "C", "F", "T", "X",
  "7", "Y", "G", "8", "B", "H", "U", "V",
  "9", "I", "J", "0", "M", "K", "O", "N",
  "+", "P", "L", "-", ".", ":", "@", ",",
  "Pound", "*", ";", "Home", "Right Shift", "=", "^", "/",
  "1", "<-", "Ctrl", "2", "Space", "CBM", "Q", "Stop",
  "Joystick Up", "Joystick Down", "Joystick Left", "Joystick Right", "Joystick Fire", "Joystick Fire 2", "Joystick Fire 3", "Joystick Fire 4",
  "2nd Joystick Up", "2nd Joystick Down", "2nd Joystick Left", "2nd Joystick Right", "2nd Joystick Fire", "2nd Joystick Fire 2", "2nd Joystick Fire 3", "2nd Joystick Fire 4",
  "none", "Shift Lock", "Restore", "Reset", "Total Reset", "Stop Emulation", "Change Disk", "Control Center",
  "Swap Joysticks"
};

proc(none)
  ret
endp

proc(PressShiftLock)
  xor mvar(bShiftLock),0x80
  ret
endp

proc(PressRestore)
  SetLow(mvar(Restore), 1)
  ret
endp

proc(ReleaseRestore)
  SetHigh(mvar(Restore), 1)
  ret
endp

proc(PressReset)
  SetLow(mvar(Reset), 1)
  ret
endp

proc(ReleaseReset)
  SetHigh(mvar(Reset), 1)
  ret
endp

proc(StopEmulation)
  mov mvar(bStopEmulation),1
  ret
endp

extern void ChangeDisk();
proc(ChangeDisk)
  call ChangeDisk
  ret
endp

extern flag Control();
proc(Control)
  call Control
  and EAX,EAX
  je NoChanges
  mov mvar(bStopEmulation),1
NoChanges:
  ret
endp

extern void fn(SwapJoysticks)();

pfnv gapfnOnSet[64] = {
  fn(none), fn(PressShiftLock), fn(PressRestore), fn(PressReset), fn(PressReset), fn(StopEmulation), fn(none), fn(Control),
  fn(SwapJoysticks),
};

pfnv gapfnOnRelease[64] = {
  fn(none), fn(none), fn(ReleaseRestore), fn(ReleaseReset), fn(ReleaseReset), fn(none), fn(ChangeDisk), fn(none),
  fn(none),
};


char* gapcPCKeys[512] = {
                  /* 0x0000 */ NULL,
  /* VK_LBUTTON      0x0001 */ "Left Mouse Button",
  /* VK_RBUTTON      0x0002 */ "Right Mouse Button",
  /* VK_CANCEL       0x0003 */ "Cancel",
  /* VK_MBUTTON      0x0004 */ "Middle Mouse Button",
                  /* 0x0005 */ NULL,
                  /* 0x0006 */ NULL,
                  /* 0x0007 */ NULL,
  /* VK_BACK         0x0008 */ "Backspace",
  /* VK_TAB          0x0009 */ "Tab",
                  /* 0x000A */ NULL,
                  /* 0x000B */ NULL,
  /* VK_CLEAR        0x000C */ "Clear",
  /* VK_RETURN       0x000D */ "Enter",
                  /* 0x000E */ NULL,
                  /* 0x000F */ NULL,
  /* VK_SHIFT        0x0010 */ "Shift",
  /* VK_CONTROL      0x0011 */ "Ctrl",
  /* VK_MENU         0x0012 */ "Alt",
  /* VK_PAUSE        0x0013 */ "Pause",
  /* VK_CAPITAL      0x0014 */ "Shift Lock",
                  /* 0x0015 */ NULL,
                  /* 0x0016 */ NULL,
                  /* 0x0017 */ NULL,
                  /* 0x0018 */ NULL,
                  /* 0x0019 */ NULL,
                  /* 0x001A */ NULL,
  /* VK_ESCAPE       0x001B */ "Esc",
                  /* 0x001C */ NULL,
                  /* 0x001D */ NULL,
                  /* 0x001E */ NULL,
                  /* 0x001F */ NULL,
  /* VK_SPACE        0x0020 */ "Space",
  /* VK_PRIOR        0x0021 */ "Num Page Up",
  /* VK_NEXT         0x0022 */ "Num Page Down",
  /* VK_END          0x0023 */ "Num End",
  /* VK_HOME         0x0024 */ "Num Home",
  /* VK_LEFT         0x0025 */ "Num Cursor Left",
  /* VK_UP           0x0026 */ "Num Cursor Up",
  /* VK_RIGHT        0x0027 */ "Num Cursor Right",
  /* VK_DOWN         0x0028 */ "Num Cursor Down",
  /* VK_SELECT       0x0029 */ "Select",
  /* VK_PRINT        0x002A */ "Print",
  /* VK_EXECUTE      0x002B */ "Execute",
  /* VK_SNAPSHOT     0x002C */ "Snapshot",
  /* VK_INSERT       0x002D */ "Num Ins",
  /* VK_DELETE       0x002E */ "Num Del",
  /* VK_HELP         0x002F */ "Help",
                  /* 0x0030 */ "0",
                  /* 0x0031 */ "1",
                  /* 0x0032 */ "2",
                  /* 0x0033 */ "3",
                  /* 0x0034 */ "4",
                  /* 0x0035 */ "5",
                  /* 0x0036 */ "6",
                  /* 0x0037 */ "7",
                  /* 0x0038 */ "8",
                  /* 0x0039 */ "9",
                  /* 0x003A */ NULL,
                  /* 0x003B */ NULL,
                  /* 0x003C */ NULL,
                  /* 0x003D */ NULL,
                  /* 0x003E */ NULL,
                  /* 0x003F */ NULL,
                  /* 0x0040 */ NULL,
                  /* 0x0041 */ "A",
                  /* 0x0042 */ "B",
                  /* 0x0043 */ "C",
                  /* 0x0044 */ "D",
                  /* 0x0045 */ "E",
                  /* 0x0046 */ "F",
                  /* 0x0047 */ "G",
                  /* 0x0048 */ "H",
                  /* 0x0049 */ "I",
                  /* 0x004A */ "J",
                  /* 0x004B */ "K",
                  /* 0x004C */ "L",
                  /* 0x004D */ "M",
                  /* 0x004E */ "N",
                  /* 0x004F */ "O",
                  /* 0x0050 */ "P",
                  /* 0x0051 */ "Q",
                  /* 0x0052 */ "R",
                  /* 0x0053 */ "S",
                  /* 0x0054 */ "T",
                  /* 0x0055 */ "U",
                  /* 0x0056 */ "V",
                  /* 0x0057 */ "W",
                  /* 0x0058 */ "X",
                  /* 0x0059 */ "Y",
                  /* 0x005A */ "Z",
  /* VK_LWIN         0x005B */ "Win",
  /* VK_RWIN         0x005C */ "Right Win",
  /* VK_APPS         0x005D */ "Apps",
                  /* 0x005E */ NULL,
                  /* 0x005F */ NULL,
  /* VK_NUMPAD0      0x0060 */ "Num 0",
  /* VK_NUMPAD1      0x0061 */ "Num 1",
  /* VK_NUMPAD2      0x0062 */ "Num 2",
  /* VK_NUMPAD3      0x0063 */ "Num 3",
  /* VK_NUMPAD4      0x0064 */ "Num 4",
  /* VK_NUMPAD5      0x0065 */ "Num 5",
  /* VK_NUMPAD6      0x0066 */ "Num 6",
  /* VK_NUMPAD7      0x0067 */ "Num 7",
  /* VK_NUMPAD8      0x0068 */ "Num 8",
  /* VK_NUMPAD9      0x0069 */ "Num 9",
  /* VK_MULTIPLY     0x006A */ NULL,
  /* VK_ADD          0x006B */ NULL,
  /* VK_SEPARATOR    0x006C */ NULL,
  /* VK_SUBTRACT     0x006D */ NULL,
  /* VK_DECIMAL      0x006E */ NULL,
  /* VK_DIVIDE       0x006F */ NULL,
  /* VK_F1           0x0070 */ "F1",
  /* VK_F2           0x0071 */ "F2",
  /* VK_F3           0x0072 */ "F3",
  /* VK_F4           0x0073 */ "F4",
  /* VK_F5           0x0074 */ "F5",
  /* VK_F6           0x0075 */ "F6",
  /* VK_F7           0x0076 */ "F7",
  /* VK_F8           0x0077 */ "F8",
  /* VK_F9           0x0078 */ "F9",
  /* VK_F10          0x0079 */ "F10",
  /* VK_F11          0x007A */ "F11",
  /* VK_F12          0x007B */ "F12",
  /* VK_F13          0x007C */ "F13",
  /* VK_F14          0x007D */ "F14",
  /* VK_F15          0x007E */ "F15",
  /* VK_F16          0x007F */ "F16",
  /* VK_F17          0x0080 */ "F17",
  /* VK_F18          0x0081 */ "F18",
  /* VK_F19          0x0082 */ "F19",
  /* VK_F20          0x0083 */ "F20",
  /* VK_F21          0x0084 */ "F21",
  /* VK_F22          0x0085 */ "F22",
  /* VK_F23          0x0086 */ "F23",
  /* VK_F24          0x0087 */ "F24",
                  /* 0x0088 */ NULL,
                  /* 0x0089 */ NULL,
                  /* 0x008A */ NULL,
                  /* 0x008B */ NULL,
                  /* 0x008C */ NULL,
                  /* 0x008D */ NULL,
                  /* 0x008E */ NULL,
                  /* 0x008F */ NULL,
  /* VK_NUMLOCK      0x0090 */ NULL,
  /* VK_SCROLL       0x0091 */ "Scroll Lock",
                  /* 0x0092 */ NULL,
                  /* 0x0093 */ NULL,
                  /* 0x0094 */ NULL,
                  /* 0x0095 */ NULL,
                  /* 0x0096 */ NULL,
                  /* 0x0097 */ NULL,
                  /* 0x0098 */ NULL,
                  /* 0x0099 */ NULL,
                  /* 0x009A */ NULL,
                  /* 0x009B */ NULL,
                  /* 0x009C */ NULL,
                  /* 0x009D */ NULL,
                  /* 0x009E */ NULL,
                  /* 0x009F */ NULL,
  /* VK_LSHIFT       0x00A0 */ NULL,
  /* VK_RSHIFT       0x00A1 */ NULL,
  /* VK_LCONTROL     0x00A2 */ NULL,
  /* VK_RCONTROL     0x00A3 */ NULL,
  /* VK_LMENU        0x00A4 */ NULL,
  /* VK_RMENU        0x00A5 */ NULL,
                  /* 0x00A6 */ NULL,
                  /* 0x00A7 */ NULL,
                  /* 0x00A8 */ NULL,
                  /* 0x00A9 */ NULL,
                  /* 0x00AA */ NULL,
                  /* 0x00AB */ NULL,
                  /* 0x00AC */ NULL,
                  /* 0x00AD */ NULL,
                  /* 0x00AE */ NULL,
                  /* 0x00AF */ NULL,
                  /* 0x00B0 */ NULL,
                  /* 0x00B1 */ NULL,
                  /* 0x00B2 */ NULL,
                  /* 0x00B3 */ NULL,
                  /* 0x00B4 */ NULL,
                  /* 0x00B5 */ NULL,
                  /* 0x00B6 */ NULL,
                  /* 0x00B7 */ NULL,
                  /* 0x00B8 */ NULL,
                  /* 0x00B9 */ NULL,
                  /* 0x00BA */ NULL,
                  /* 0x00BB */ NULL,
                  /* 0x00BC */ NULL,
                  /* 0x00BD */ NULL,
                  /* 0x00BE */ NULL,
                  /* 0x00BF */ NULL,
                  /* 0x00C0 */ NULL,
                  /* 0x00C1 */ NULL,
                  /* 0x00C2 */ NULL,
                  /* 0x00C3 */ NULL,
                  /* 0x00C4 */ NULL,
                  /* 0x00C5 */ NULL,
                  /* 0x00C6 */ NULL,
                  /* 0x00C7 */ NULL,
                  /* 0x00C8 */ NULL,
                  /* 0x00C9 */ NULL,
                  /* 0x00CA */ NULL,
                  /* 0x00CB */ NULL,
                  /* 0x00CC */ NULL,
                  /* 0x00CD */ NULL,
                  /* 0x00CE */ NULL,
                  /* 0x00CF */ NULL,
                  /* 0x00D0 */ NULL,
                  /* 0x00D1 */ NULL,
                  /* 0x00D2 */ NULL,
                  /* 0x00D3 */ NULL,
                  /* 0x00D4 */ NULL,
                  /* 0x00D5 */ NULL,
                  /* 0x00D6 */ NULL,
                  /* 0x00D7 */ NULL,
                  /* 0x00D8 */ NULL,
                  /* 0x00D9 */ NULL,
                  /* 0x00DA */ NULL,
                  /* 0x00DB */ NULL,
                  /* 0x00DC */ NULL,
                  /* 0x00DD */ NULL,
                  /* 0x00DE */ NULL,
                  /* 0x00DF */ NULL,
                  /* 0x00E0 */ NULL,
                  /* 0x00E1 */ NULL,
                  /* 0x00E2 */ NULL,
                  /* 0x00E3 */ NULL,
                  /* 0x00E4 */ NULL,
                  /* 0x00E5 */ NULL,
                  /* 0x00E6 */ NULL,
                  /* 0x00E7 */ NULL,
                  /* 0x00E8 */ NULL,
                  /* 0x00E9 */ NULL,
                  /* 0x00EA */ NULL,
                  /* 0x00EB */ NULL,
                  /* 0x00EC */ NULL,
                  /* 0x00ED */ NULL,
                  /* 0x00EE */ NULL,
                  /* 0x00EF */ NULL,
                  /* 0x00F0 */ NULL,
                  /* 0x00F1 */ NULL,
                  /* 0x00F2 */ NULL,
                  /* 0x00F3 */ NULL,
                  /* 0x00F4 */ NULL,
                  /* 0x00F5 */ NULL,
  /* VK_ATTN         0x00F6 */ NULL,
  /* VK_CRSEL        0x00F7 */ NULL,
  /* VK_EXSEL        0x00F8 */ NULL,
  /* VK_EREOF        0x00F9 */ NULL,
  /* VK_PLAY         0x00FA */ NULL,
  /* VK_ZOOM         0x00FB */ NULL,
  /* VK_NONAME       0x00FC */ NULL,
  /* VK_PA1          0x00FD */ NULL,
  /* VK_OEM_CLEAR    0x00FE */ NULL,
                  /* 0x00FF */ NULL,
                  /* 0x0100 */ NULL,
  /* VK_LBUTTON      0x0101 */ NULL,
  /* VK_RBUTTON      0x0102 */ NULL,
  /* VK_CANCEL       0x0103 */ NULL,
  /* VK_MBUTTON      0x0104 */ NULL,
                  /* 0x0105 */ NULL,
                  /* 0x0106 */ NULL,
                  /* 0x0107 */ NULL,
  /* VK_BACK         0x0108 */ NULL,
  /* VK_TAB          0x0109 */ NULL,
                  /* 0x010A */ NULL,
                  /* 0x010B */ NULL,
  /* VK_CLEAR        0x010C */ NULL,
  /* VK_RETURN       0x010D */ "Num Enter",
                  /* 0x010E */ NULL,
                  /* 0x010F */ NULL,
  /* VK_SHIFT        0x0110 */ "Right Shift",
  /* VK_CONTROL      0x0111 */ "Right Ctrl",
  /* VK_MENU         0x0112 */ "Right Alt",
  /* VK_PAUSE        0x0113 */ NULL,
  /* VK_CAPITAL      0x0114 */ NULL,
                  /* 0x0115 */ NULL,
                  /* 0x0116 */ NULL,
                  /* 0x0117 */ NULL,
                  /* 0x0118 */ NULL,
                  /* 0x0119 */ NULL,
                  /* 0x011A */ NULL,
  /* VK_ESCAPE       0x011B */ NULL,
                  /* 0x011C */ NULL,
                  /* 0x011D */ NULL,
                  /* 0x011E */ NULL,
                  /* 0x011F */ NULL,
  /* VK_SPACE        0x0120 */ NULL,
  /* VK_PRIOR        0x0121 */ "Page Up",
  /* VK_NEXT         0x0122 */ "Page Down",
  /* VK_END          0x0123 */ "End",
  /* VK_HOME         0x0124 */ "Home",
  /* VK_LEFT         0x0125 */ "Cursor Left",
  /* VK_UP           0x0126 */ "Cursor Up",
  /* VK_RIGHT        0x0127 */ "Cursor Right",
  /* VK_DOWN         0x0128 */ "Cursor Down",
  /* VK_SELECT       0x0129 */ NULL,
  /* VK_PRINT        0x012A */ NULL,
  /* VK_EXECUTE      0x012B */ NULL,
  /* VK_SNAPSHOT     0x012C */ NULL,
  /* VK_INSERT       0x012D */ "Ins",
  /* VK_DELETE       0x012E */ "Del",
  /* VK_HELP         0x012F */ NULL,
                  /* 0x0130 */ NULL,
                  /* 0x0131 */ NULL,
                  /* 0x0132 */ NULL,
                  /* 0x0133 */ NULL,
                  /* 0x0134 */ NULL,
                  /* 0x0135 */ NULL,
                  /* 0x0136 */ NULL,
                  /* 0x0137 */ NULL,
                  /* 0x0138 */ NULL,
                  /* 0x0139 */ NULL,
                  /* 0x013A */ NULL,
                  /* 0x013B */ NULL,
                  /* 0x013C */ NULL,
                  /* 0x013D */ NULL,
                  /* 0x013E */ NULL,
                  /* 0x013F */ NULL,
                  /* 0x0140 */ NULL,
                  /* 0x0141 */ NULL,
                  /* 0x0142 */ NULL,
                  /* 0x0143 */ NULL,
                  /* 0x0144 */ NULL,
                  /* 0x0145 */ NULL,
                  /* 0x0146 */ NULL,
                  /* 0x0147 */ NULL,
                  /* 0x0148 */ NULL,
                  /* 0x0149 */ NULL,
                  /* 0x014A */ NULL,
                  /* 0x014B */ NULL,
                  /* 0x014C */ NULL,
                  /* 0x014D */ NULL,
                  /* 0x014E */ NULL,
                  /* 0x014F */ NULL,
                  /* 0x0150 */ NULL,
                  /* 0x0151 */ NULL,
                  /* 0x0152 */ NULL,
                  /* 0x0153 */ NULL,
                  /* 0x0154 */ NULL,
                  /* 0x0155 */ NULL,
                  /* 0x0156 */ NULL,
                  /* 0x0157 */ NULL,
                  /* 0x0158 */ NULL,
                  /* 0x0159 */ NULL,
                  /* 0x015A */ NULL,
  /* VK_LWIN         0x015B */ NULL,
  /* VK_RWIN         0x015C */ NULL,
  /* VK_APPS         0x015D */ NULL,
                  /* 0x015E */ NULL,
                  /* 0x015F */ NULL,
  /* VK_NUMPAD0      0x0160 */ NULL,
  /* VK_NUMPAD1      0x0161 */ NULL,
  /* VK_NUMPAD2      0x0162 */ NULL,
  /* VK_NUMPAD3      0x0163 */ NULL,
  /* VK_NUMPAD4      0x0164 */ NULL,
  /* VK_NUMPAD5      0x0165 */ NULL,
  /* VK_NUMPAD6      0x0166 */ NULL,
  /* VK_NUMPAD7      0x0167 */ NULL,
  /* VK_NUMPAD8      0x0168 */ NULL,
  /* VK_NUMPAD9      0x0169 */ NULL,
  /* VK_MULTIPLY     0x016A */ "Num *",
  /* VK_ADD          0x016B */ "Num +",
  /* VK_SEPARATOR    0x016C */ NULL,
  /* VK_SUBTRACT     0x016D */ "Num -",
  /* VK_DECIMAL      0x016E */ NULL,
  /* VK_DIVIDE       0x016F */ "Num /",
  /* VK_F1           0x0170 */ NULL,
  /* VK_F2           0x0171 */ NULL,
  /* VK_F3           0x0172 */ NULL,
  /* VK_F4           0x0173 */ NULL,
  /* VK_F5           0x0174 */ NULL,
  /* VK_F6           0x0175 */ NULL,
  /* VK_F7           0x0176 */ NULL,
  /* VK_F8           0x0177 */ NULL,
  /* VK_F9           0x0178 */ NULL,
  /* VK_F10          0x0179 */ NULL,
  /* VK_F11          0x017A */ NULL,
  /* VK_F12          0x017B */ NULL,
  /* VK_F13          0x017C */ NULL,
  /* VK_F14          0x017D */ NULL,
  /* VK_F15          0x017E */ NULL,
  /* VK_F16          0x017F */ NULL,
  /* VK_F17          0x0180 */ NULL,
  /* VK_F18          0x0181 */ NULL,
  /* VK_F19          0x0182 */ NULL,
  /* VK_F20          0x0183 */ NULL,
  /* VK_F21          0x0184 */ NULL,
  /* VK_F22          0x0185 */ NULL,
  /* VK_F23          0x0186 */ NULL,
  /* VK_F24          0x0187 */ NULL,
                  /* 0x0188 */ NULL,
                  /* 0x0189 */ NULL,
                  /* 0x018A */ NULL,
                  /* 0x018B */ NULL,
                  /* 0x018C */ NULL,
                  /* 0x018D */ NULL,
                  /* 0x018E */ NULL,
                  /* 0x018F */ NULL,
  /* VK_NUMLOCK      0x0190 */ "Num Lock",
  /* VK_SCROLL       0x0191 */ NULL,
                  /* 0x0192 */ NULL,
                  /* 0x0193 */ NULL,
                  /* 0x0194 */ NULL,
                  /* 0x0195 */ NULL,
                  /* 0x0196 */ NULL,
                  /* 0x0197 */ NULL,
                  /* 0x0198 */ NULL,
                  /* 0x0199 */ NULL,
                  /* 0x019A */ NULL,
                  /* 0x019B */ NULL,
                  /* 0x019C */ NULL,
                  /* 0x019D */ NULL,
                  /* 0x019E */ NULL,
                  /* 0x019F */ NULL,
  /* VK_LSHIFT       0x01A0 */ NULL,
  /* VK_RSHIFT       0x01A1 */ NULL,
  /* VK_LCONTROL     0x01A2 */ NULL,
  /* VK_RCONTROL     0x01A3 */ NULL,
  /* VK_LMENU        0x01A4 */ NULL,
  /* VK_RMENU        0x01A5 */ NULL,
                  /* 0x01A6 */ NULL,
                  /* 0x01A7 */ NULL,
                  /* 0x01A8 */ NULL,
                  /* 0x01A9 */ NULL,
                  /* 0x01AA */ NULL,
                  /* 0x01AB */ NULL,
                  /* 0x01AC */ NULL,
                  /* 0x01AD */ NULL,
                  /* 0x01AE */ NULL,
                  /* 0x01AF */ NULL,
                  /* 0x01B0 */ NULL,
                  /* 0x01B1 */ NULL,
                  /* 0x01B2 */ NULL,
                  /* 0x01B3 */ NULL,
                  /* 0x01B4 */ NULL,
                  /* 0x01B5 */ NULL,
                  /* 0x01B6 */ NULL,
                  /* 0x01B7 */ NULL,
                  /* 0x01B8 */ NULL,
                  /* 0x01B9 */ NULL,
                  /* 0x01BA */ NULL,
                  /* 0x01BB */ NULL,
                  /* 0x01BC */ NULL,
                  /* 0x01BD */ NULL,
                  /* 0x01BE */ NULL,
                  /* 0x01BF */ NULL,
                  /* 0x01C0 */ NULL,
                  /* 0x01C1 */ NULL,
                  /* 0x01C2 */ NULL,
                  /* 0x01C3 */ NULL,
                  /* 0x01C4 */ NULL,
                  /* 0x01C5 */ NULL,
                  /* 0x01C6 */ NULL,
                  /* 0x01C7 */ NULL,
                  /* 0x01C8 */ NULL,
                  /* 0x01C9 */ NULL,
                  /* 0x01CA */ NULL,
                  /* 0x01CB */ NULL,
                  /* 0x01CC */ NULL,
                  /* 0x01CD */ NULL,
                  /* 0x01CE */ NULL,
                  /* 0x01CF */ NULL,
                  /* 0x01D0 */ NULL,
                  /* 0x01D1 */ NULL,
                  /* 0x01D2 */ NULL,
                  /* 0x01D3 */ NULL,
                  /* 0x01D4 */ NULL,
                  /* 0x01D5 */ NULL,
                  /* 0x01D6 */ NULL,
                  /* 0x01D7 */ NULL,
                  /* 0x01D8 */ NULL,
                  /* 0x01D9 */ NULL,
                  /* 0x01DA */ NULL,
                  /* 0x01DB */ NULL,
                  /* 0x01DC */ NULL,
                  /* 0x01DD */ NULL,
                  /* 0x01DE */ NULL,
                  /* 0x01DF */ NULL,
                  /* 0x01E0 */ NULL,
                  /* 0x01E1 */ NULL,
                  /* 0x01E2 */ NULL,
                  /* 0x01E3 */ NULL,
                  /* 0x01E4 */ NULL,
                  /* 0x01E5 */ NULL,
                  /* 0x01E6 */ NULL,
                  /* 0x01E7 */ NULL,
                  /* 0x01E8 */ NULL,
                  /* 0x01E9 */ NULL,
                  /* 0x01EA */ NULL,
                  /* 0x01EB */ NULL,
                  /* 0x01EC */ NULL,
                  /* 0x01ED */ NULL,
                  /* 0x01EE */ NULL,
                  /* 0x01EF */ NULL,
                  /* 0x01F0 */ NULL,
                  /* 0x01F1 */ NULL,
                  /* 0x01F2 */ NULL,
                  /* 0x01F3 */ NULL,
                  /* 0x01F4 */ NULL,
                  /* 0x01F5 */ NULL,
  /* VK_ATTN         0x01F6 */ NULL,
  /* VK_CRSEL        0x01F7 */ NULL,
  /* VK_EXSEL        0x01F8 */ NULL,
  /* VK_EREOF        0x01F9 */ NULL,
  /* VK_PLAY         0x01FA */ NULL,
  /* VK_ZOOM         0x01FB */ NULL,
  /* VK_NONAME       0x01FC */ NULL,
  /* VK_PA1          0x01FD */ NULL,
  /* VK_OEM_CLEAR    0x01FE */ NULL,
                  /* 0x01FF */ NULL
};

char CKeyboard::GetChar() {
  if (*gpcTextIn == '«') {
    while (gpcTextIn < gpcTextEnd && *gpcTextIn != '»' && *gpcTextIn != '\r') {
      gpcTextIn++;
    }
    if (gpcTextIn < gpcTextEnd) {
      gpcTextIn++;
    }
  }
  return *gpcTextIn;
}

void CKeyboard::SkipSpace(flag fSkipComment) {
  while (gpcTextIn < gpcTextEnd) {
    switch (GetChar()) {
    case ' ':
    case '\t':
      gpcTextIn++;
      break;
    case ';':
      if (fSkipComment) {
        return;
      }
    case '\r':
      while (gpcTextIn < gpcTextEnd && *gpcTextIn != '\n') {
        *gpcTextOut++ = *gpcTextIn++;
      }
      break;
    default:
      return;
    }
  }
}

void CKeyboard::Insert(char* pcText) {
  int iLength = strlen(pcText);
  memcpy(gpcTextOut, pcText, iLength);
  gpcTextOut += iLength;
  while (gpcTextIn < gpcTextEnd) {
    switch (GetChar()) {
    case '\n':
      return;
    case ';':
      while (gpcTextIn < gpcTextEnd && *gpcTextIn != '\n') {
        *gpcTextOut++ = *gpcTextIn++;
      }
      break;
    default:
      *gpcTextOut++ = *gpcTextIn++;
    }
  }
}

flag CKeyboard::Find(char** ppcText, int iCount) {
  *gpbOut = 1;
  for (;;) {
    SkipSpace(TRUE);
    int iFound = -1;
    int iMaxLength = 0;
    for (int i = 0; i < iCount; i++) {
      if (ppcText[i] != NULL) {
        int iLength = strlen(ppcText[i]);
        if (gpcTextIn + iLength <= gpcTextEnd && !isalnum(gpcTextIn[iLength])) {
          if (_memicmp(ppcText[i], gpcTextIn, iLength) == 0) {
            if (iLength > iMaxLength) {
              iMaxLength = iLength;
              iFound = i;
            }  
          }
        }
      }
    }
    if (iFound < 0) {
      return FALSE;
    }
    gpbOut[*gpbOut] = (byte)iFound;
    (*gpbOut)++;
    if (iCount > 256) {
      gpbOut[*gpbOut] = (byte)(iFound >> 8);
      (*gpbOut)++;
    }
    memcpy(gpcTextOut, ppcText[iFound], iMaxLength);
    gpcTextIn += iMaxLength;
    gpcTextOut += iMaxLength;
    SkipSpace();
    if (GetChar() != '+') {
      return TRUE;
    }
    gpcTextIn++;
    *gpcTextOut++ = '+';
  }
}

void CKeyboard::GetC64Keys() {
  SkipSpace();
  if (GetChar() != '=') {
    Insert("«= expected»");
    return;
  }
  gpcTextIn++;
  *gpcTextOut++ = ' ';
  *gpcTextOut++ = '=';
  *gpcTextOut++ = ' ';
  do {
    if (!Find(gapcC64Keys, sizeof gapcC64Keys / sizeof gapcC64Keys[0])) {
      Insert("«unknown C64 key»");
      return;
    }
    gpbOut += *gpbOut;
    if (GetChar() == ',') {
      gpcTextIn++;
      *gpcTextOut++ = ',';
      *gpcTextOut++ = ' ';
    } else if (GetChar() == '\n') {
      return;
    } else {
      Insert("«+ , or end of line expected»");
      return;
    }
  } while (gpcTextIn < gpcTextEnd);
  Insert("«C64 key(s) expected»");
}

void CKeyboard::Parse() {
  while (gpcTextIn < gpcTextEnd) {
    SkipSpace();
    switch (GetChar()) {
    case '\n':
      *gpcTextOut++ = *gpcTextIn++;
      break;
    case '\'':
      *gpcTextOut++ = *gpcTextIn++;
      *gpbOut = 0x42;
      gpbOut[1] = *gpcTextIn;
      *gpcTextOut++ = *gpcTextIn++;
      if (GetChar() != '\'') {
        Insert("«closing ' expected»");
        break;
      }
      *gpcTextOut++ = *gpcTextIn++;
      gpbOut += 2;
      GetC64Keys();
      break;
    default:
      if (!Find(gapcPCKeys, sizeof gapcPCKeys / sizeof gapcPCKeys[0])) {
        Insert("«unknown PC key»");
        break;
      }
      *gpbOut |= 0x80;
      gpbOut += (*gpbOut & 0x3F);
      GetC64Keys();
      break;
    }
  }
}

void CKeyboard::ReadKeyMapping() {
  char* pcIn = (char*)MemAlloc(65536);
  char* pcOut = (char*)MemAlloc(65536);
  byte* pbKeys = (byte*)MemAlloc(8192);
  HANDLE hFile = OpenFile(GetProgramDirectory("Keys.ini"));
  int iSize = GetFileSize(hFile, NULL);
  if (iSize > 65535) {
    error("Keys.ini is bigger than 65535 bytes");
  }
  ReadFile(hFile, pcIn, iSize);
  gpcTextIn = pcIn;
  gpcTextEnd = pcIn + iSize;
  *gpcTextEnd = 0;
  gpcTextOut = pcOut;
  gpbOut = pbKeys;
  Parse();
  *gpbOut++ = 0;
  SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
  WriteFile(hFile, pcOut, gpcTextOut - pcOut);
  SetEndOfFile(hFile);
  CloseHandle(hFile);
  iSize = gpbOut - pbKeys;
  ASSERT(iSize <= 8192);
  gpbKeyMapping = (byte*)MemAlloc(iSize);
  memcpy(gpbKeyMapping, pbKeys, iSize);
  MemFree(pbKeys);
  MemFree(pcOut);
  MemFree(pcIn);
}

/*
The mapping of PC keys to C64 keys

Once at start, the file Keys.txt is parsed and read into gpbKeyMapping.
The format in memory is:

  $41+1, ANSI Char, 1+n, n*C64 keys [, 1+n, n*C64 keys ...]
  $81+n, n/2*PC keys 16 bit, 1+n, n*C64 keys [, 1+n, n*C64 keys ...]
  $00 end of table

If the user has pressed a key, Windows will send a WM_KEYDOWN message to
the window which has the focus. It passes the keystroke to C64Key().

The virtual scan code is in wParam. If the extension bit 24 in lParam is
set, 256 is added to the scan code. After that, the corresponding byte in
gabKeyState[] is set or released.

If the key has been pressed (bit 31 of lParam is clear), C64Key() tries to
translate the vitual key code into a character and stores it in bChar. If
this didn't work, bChar is set to 0.

Now a copy of gabKeyState is made and the gpbKeyMapping array is searched.
Each time a key combination is found, the corresponding bytes in the copy
of gabKeyState will be set back to 0. The search is finished if the end of
the table is reached or a character match in bChar is found.

If a match is found, the C64 keys are written into the ring buffer
gabC64Keys[].

*/

void CKeyboard::SetKeyMatrix() {
  if (gbC64Out == gbC64In) {
    return;
  }
  // wait -n/50 seconds
  if (gabC64Keys[gbC64Out] >= 0xF0) {
    gabC64Keys[gbC64Out]++;
    if (gabC64Keys[gbC64Out] == 0) {
      gbC64Out++;
    }
    return;
  }
  // clear bits in the keyboard matrix
  long lChange = *(long*)(abMatrix + 10);
  memset(abMatrix, 0xFF, sizeof abMatrix);
  while (gbC64Out != gbC64In && gabC64Keys[gbC64Out] < 0xF0) {
    byte bKey = gabC64Keys[gbC64Out++];
    abMatrix[bKey >> 3 & 15] &= (byte)~(0x01 << (bKey & 7));
  }
  // pass changes to CIA
  __asm {
    push EBX
    push ThisReg
    mov ThisReg,this
    GetPort(mvar(KeyRow))
    call fn(SetKeyRow)
    mov AL,mvar(abMatrix[8])
    SetPort(mvar(Joystick), 1)
    mov AL,mvar(abMatrix[9])
    SetPort(mvar(SecondJoystick), 2)
    pop ThisReg
    pop EBX
  }
  // call functions
  lChange ^= *(long*)(abMatrix + 10);
  if (lChange > 1) {
    for (int i = 1; i < 32; i++) {
      if ((lChange & 1 << i) != 0) {
        pfnv pfnAction;
        if ((*(long*)(abMatrix + 10) & 1 << i) == 0) {
          pfnAction = gapfnOnSet[i];
        } else {
          pfnAction = gapfnOnRelease[i];
        }
        __asm {
          push EBX
          push ThisReg
          mov ThisReg,this
          call pfnAction
          pop ThisReg
          pop EBX
        }
      }
    }
  }
}

byte* CKeyboard::AddC64Keys(byte* pb) {
  for (;;) {
    for (int i = 1; i < *pb; i++) {
      gabC64Keys[gbC64In++] = pb[i];
    }
    pb += *pb;
    if (*pb == 0 || (*pb & 0xC0) != 0) {
      break;
    }
    gabC64Keys[gbC64In++] = bWait;
  }
  return pb;
}

void CKeyboard::C64Key(WPARAM wParam, LPARAM lParam) {
  #if _TRACE
    trace("%s %04X (%02X)", lParam < 0 ? "Release" : "Set", wParam, lParam >> 16 & 0xFF);
  #endif
  if (wParam < 0x0100) {
    // no difference between Left Shift and Right Shift
    if (wParam == VK_SHIFT && (lParam & 0x007F0000) == 0x00360000) {
      lParam |= 1 << 24;
    }
    // map extended keys
    if (lParam & 1 << 24) {
      wParam |= 0x0100;
    }
    #if _TRACE
      trace("  -> %s\n", gapcPCKeys[wParam]);
    #endif
    // set or release key
    if (lParam >= 0) {
      if (gabKeyState[wParam]) {
        // ignore repeats
        return;
      }
      gabKeyState[wParam] = 1;
    } else {
      gabKeyState[wParam] = 0;
    }
  }
  // translate key to ANSI char
  byte bChar = 0;
  if (lParam >= 0) {
    BYTE abState[256];
    GetKeyboardState(abState);
    WORD awChar[2];
    int iChars = ToAscii(wParam, lParam, abState, awChar, FALSE);
    ASSERT(iChars <= 2);
    if (iChars) {
      bChar = (byte)awChar[iChars - 1];
      if (abState[VK_MENU] & 0x80) {
        // ignore menu combinations with Alt
        abState[VK_MENU] = 0;
        int iChars = ToAscii(wParam, lParam, abState, awChar, FALSE);
        ASSERT(iChars <= 2);
        if (iChars && (byte)awChar[iChars - 1] == bChar) {
          bChar = 0;
        }
      }
    }
  }
  // clear last keystroke
  gabC64Keys[gbC64In++] = bNone;
  // search for keys or char
  byte abKeyState[512];
  memcpy(abKeyState, gabKeyState, sizeof abKeyState);
  byte* pb = gpbKeyMapping;
  while (*pb != 0) {
    switch (*pb & 0xC0) {
    case 0x00:
      pb += *pb;
      break;
    case 0x40:
      ASSERT(*pb == 0x42);
      pb += 2;
      if (pb[-1] == bChar) {
        AddC64Keys(pb);
        gabC64Keys[gbC64In++] = bWait;
        return;
      }
      break;
    case 0x80:
      {
        for (int i = 1; i < (*pb & 0x3F); i += 2) {
          if (abKeyState[*(short*)(pb + i)] == 0) {
            pb += *pb & 0x3F;
            goto Skip;
          }
        }
        for (i = 1; i < (*pb & 0x3F); i += 2) {
          abKeyState[*(short*)(pb + i)] = 0;
        }
        pb += *pb & 0x3F;
        pb = AddC64Keys(pb);
      Skip:
        break;
      }
    default:
      error("unknown key code in gpbKeyMapping");
    }
  }
  gabC64Keys[gbC64In++] = bWait;
}

void CKeyboard::ClearKeys() {
  for (int i = 0; i < 512; i++) {
    if (gabKeyState[i] != 0) {
      C64Key(i & 0xFF, i < 0x0100 ? 0x80000000 : 0x80000000 |  (1 << 24));
      ASSERT(gabKeyState[i] == 0);
    }
  }
}

/*
  CPort KeyRow;
  CPort KeyCol;
  CLine Restore;
  CLine Reset;
  CPort Joystick;
  CPort SecondJoystick;
  byte abMatrix[16];
  byte gabKeyState[512];
  byte gabC64Keys[256];
  char* gpcTextIn;
  char* gpcTextEnd;
  char* gpcTextOut;
  byte* gpbOut;
  byte* gpbKeyMapping;

  byte gbC64In;
  byte gbC64Out;
  byte bShiftLock;
  byte bStopEmulation;
*/

CKeyboard::CKeyboard() {
  KeyRow.Init("KeyRow", this);
  KeyRow.SetOnChange((pfnbb)KeyOnRowChange);
  KeyCol.Init("KeyCol", this);
  KeyCol.SetOnChange((pfnbb)KeyOnColChange);
  Restore.Init("Restore", this);
  Reset.Init("Reset", this);
  Joystick.Init("Joystick", this);
  SecondJoystick.Init("SecondJoystick", this);
  memset(abMatrix, 0xFF, sizeof abMatrix);
  memset(gabKeyState, 0, sizeof gabKeyState);
  memset(gabC64Keys, 0, sizeof gabC64Keys);
  gpcTextIn = NULL;
  gpcTextEnd = NULL;
  gpcTextOut = NULL;
  gpbOut = NULL;
  gpbKeyMapping = NULL;

  gbC64In = 0;
  gbC64Out = 0;
  bShiftLock = 0xFF;
  bStopEmulation = 0;
}

CKeyboard::~CKeyboard() {
  if (gpbKeyMapping != NULL) {
    MemFree(gpbKeyMapping);
    gpbKeyMapping = NULL;
  }
}
