/*
 * Troy's HBC-56 Emulator - Keyboard device
 *
 * Copyright (c) 2021 Troy Schrapel
 *
 * This code is licensed under the MIT license
 *
 * https://github.com/visrealm/hbc-56/emulator
 *
 */

#include "keyboard_device.h"
#include "../hbc56emu.h"

#include "SDL.h"

static void resetKeyboardDevice(HBC56Device*);
static uint8_t readKeyboardDevice(HBC56Device*, uint16_t, uint8_t*, uint8_t);
static void eventKeyboardDevice(HBC56Device*, SDL_Event*);

/* map sdl scancodes to ps/2 scancodes */
static uint64_t sdl2ps2map[232][2];

#define KB_INT_FLAG 0x02
#define KB_RDY_FLAG 0x04

#define KB_QUEUE_SIZE 1024
#define KB_QUEUE_MASK (KB_QUEUE_SIZE - 1)

/* keyboard device data */
struct KeyboardDevice
{
  uint16_t  addr;
  uint8_t   irq;
  char      kbQueue[KB_QUEUE_SIZE];
  int       kbStart;
  int       kbEnd;
};
typedef struct KeyboardDevice KeyboardDevice;


/* Function:  createRamKeyboardDevice
 * --------------------
 * create a ram or rom device for the given address range
 */
HBC56Device createKeyboardDevice(
  uint16_t addr,
  uint8_t irq)
{
  HBC56Device device = createDevice("Keyboard");
  KeyboardDevice* keyboardDevice = (KeyboardDevice*)malloc(sizeof(KeyboardDevice));
  if (keyboardDevice)
  {
    SDL_memset(keyboardDevice, 0, sizeof(KeyboardDevice));
    keyboardDevice->addr = addr;
    keyboardDevice->irq = irq;
    device.data = keyboardDevice;

    device.resetFn = &resetKeyboardDevice;
    device.readFn = &readKeyboardDevice;
    device.eventFn = &eventKeyboardDevice;
  }
  else
  {
    destroyDevice(&device);
  }

  return device;
}


/* Function:  getKeyboardDevice
 * --------------------
 * helper funtion to get private structure
 */
inline static KeyboardDevice* getKeyboardDevice(HBC56Device* device)
{
  if (!device) return NULL;
  return (KeyboardDevice*)device->data;
}

static uint8_t readKeyboardDevice(HBC56Device* device, uint16_t addr, uint8_t *val, uint8_t dbg)
{
  KeyboardDevice* kbDevice = getKeyboardDevice(device);
  if (kbDevice && val)
  {
    if (addr == kbDevice->addr)
    {
      *val = 0;

      if (kbDevice->kbEnd != kbDevice->kbStart)
      {
        *val = kbDevice->kbQueue[kbDevice->kbStart++];

        kbDevice->kbStart &= KB_QUEUE_MASK;

        if (kbDevice->kbStart == kbDevice->kbEnd)
        {
          hbc56Interrupt(kbDevice->irq, INTERRUPT_RELEASE);
        }
      }
      return 1;
    }
    else if (addr == kbDevice->addr + 1)
    {
      /* status */
      *val = (kbDevice->kbStart != kbDevice->kbEnd) 
                  ? (KB_INT_FLAG | KB_RDY_FLAG)
                  : 0;
      return 1;
    }
  }
  return 0;
}


static void resetKeyboardDevice(HBC56Device* device)
{
  KeyboardDevice* kbDevice = getKeyboardDevice(device);
  if (kbDevice)
  {
    kbDevice->kbStart = 0;
    kbDevice->kbEnd = 0;
  }
}

static void eventKeyboardDevice(HBC56Device *device, SDL_Event *event)
{
  KeyboardDevice* kbDevice = getKeyboardDevice(device);
  if (kbDevice && event)
  {
    if (event->type != SDL_KEYUP && event->type != SDL_KEYDOWN) return;

    uint64_t ps2ScanCode = sdl2ps2map[event->key.keysym.scancode][event->type == SDL_KEYUP];
    for (int i = 0; i < 8; ++i)
    {
      uint8_t scanCodeByte = (ps2ScanCode & 0xff00000000000000) >> 56;
      if (scanCodeByte)
      {
        kbDevice->kbQueue[kbDevice->kbEnd++] = scanCodeByte; kbDevice->kbEnd &= KB_QUEUE_MASK;
        hbc56Interrupt(kbDevice->irq, INTERRUPT_RAISE);
      }
      ps2ScanCode <<= 8;
    }
  }
}

int keyboardDeviceQueueCap(HBC56Device* device) {
  KeyboardDevice* kbDevice = getKeyboardDevice(device);
  if (kbDevice) {
    int diff = kbDevice->kbEnd - kbDevice->kbStart;
    if (diff < 0) diff += KB_QUEUE_SIZE;  
    return KB_QUEUE_SIZE - diff;
  }
  return 0;
}

bool keyboardDeviceQueueEmpty(HBC56Device* device) {
  KeyboardDevice* kbDevice = getKeyboardDevice(device);
  if (kbDevice) {
    return kbDevice->kbEnd == kbDevice->kbStart;
  }
  return false;
}


static uint64_t sdl2ps2map[232][2] = {
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x1C, 0xF01C},
  {0x32, 0xF032},
  {0x21, 0xF021},
  {0x23, 0xF023},
  {0x24, 0xF024},
  {0x2B, 0xF02B},
  {0x34, 0xF034},
  {0x33, 0xF033},
  {0x43, 0xF043},
  {0x3B, 0xF03B},
  {0x42, 0xF042},
  {0x4B, 0xF04B},
  {0x3A, 0xF03A},
  {0x31, 0xF031},
  {0x44, 0xF044},
  {0x4D, 0xF04D},
  {0x15, 0xF015},
  {0x2D, 0xF02D},
  {0x1B, 0xF01B},
  {0x2C, 0xF02C},
  {0x3C, 0xF03C},
  {0x2A, 0xF02A},
  {0x1D, 0xF01D},
  {0x22, 0xF022},
  {0x35, 0xF035},
  {0x1A, 0xF01A},
  {0x16, 0xF016},
  {0x1E, 0xF01E},
  {0x26, 0xF026},
  {0x25, 0xF025},
  {0x2E, 0xF02E},
  {0x36, 0xF036},
  {0x3D, 0xF03D},
  {0x3E, 0xF03E},
  {0x46, 0xF046},
  {0x45, 0xF045},
  {0x5A, 0xF05A},
  {0x76, 0xF076},
  {0x66, 0xF066},
  {0x0D, 0xF00D},
  {0x29, 0xF029},
  {0x4E, 0xF04E},
  {0x55, 0xF055},
  {0x54, 0xF054},
  {0x5B, 0xF05B},
  {0x5D, 0xF05D},
  {0x00, 0x00},
  {0x4C, 0xF04C},
  {0x52, 0xF052},
  {0x0E, 0xF00E},
  {0x41, 0xF041},
  {0x49, 0xF049},
  {0x4A, 0xF04A},
  {0x58, 0xF058},
  {0x05, 0xF005},
  {0x06, 0xF006},
  {0x04, 0xF004},
  {0x0C, 0xF00C},
  {0x03, 0xF003},
  {0x0B, 0xF00B},
  {0x83, 0xF083},
  {0x0A, 0xF00A},
  {0x01, 0xF001},
  {0x09, 0xF009},
  {0x78, 0xF078},
  {0x07, 0xF007},
  {0xE012E07C, 0xE0F012E0F07C},
  {0x7E, 0xF07E},
  {0xE11477, 0xE1F014E077},
  {0xE070, 0xE0F070},
  {0xE06C, 0xE0F06C},
  {0xE07D, 0xE0F07D},
  {0xE071, 0xE0F071},
  {0xE069, 0xE0F069},
  {0xE07A, 0xE0F07A},
  {0xE074, 0xE0F074},
  {0xE06B, 0xE0F06B},
  {0xE072, 0xE0F072},
  {0xE075, 0xE0F075},
  {0xE077, 0xE0F077},
  {0xE04A, 0xE0F04A},
  {0x7C, 0xF07C},
  {0x7B, 0xF07B},
  {0x79, 0xF079},
  {0xE05A, 0xE0F05A},
  {0x69, 0xF069},
  {0x72, 0xF072},
  {0x7A, 0xF07A},
  {0x6B, 0xF06B},
  {0x73, 0xF073},
  {0x74, 0xF074},
  {0x6C, 0xF06C},
  {0x75, 0xF075},
  {0x7D, 0xF07D},
  {0x70, 0xF070},
  {0x71, 0xF071},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x00, 0x00},
  {0x14, 0xF014},
  {0x12, 0xF012},
  {0x11, 0xF011},
  {0xE02F, 0xE0F02F},
  {0xE014, 0xE0F014},
  {0x59, 0xF059},
  {0xE011, 0xE0F011},
  {0xE02F, 0xE0F02F} };
