//
// si/controller.c: Serial interface controller.
//
// CEN64: Cycle-Accurate Nintendo 64 Emulator.
// Copyright (C) 2015, Tyler J. Stachecki.
//
// This file is subject to the terms and conditions defined in
// 'LICENSE', which is part of this source code package.
//

#include "common.h"
#include "bus/address.h"
#include "bus/controller.h"
#include "gl_window.h"
#include "ri/controller.h"
#include "si/cic.h"
#include "si/controller.h"
#include "si/rtc.h"
#include "thread.h"
#include "vi/controller.h"
#include "vr4300/interface.h"
#include <assert.h>

#ifdef DEBUG_MMIO_REGISTER_ACCESS
const char *si_register_mnemonics[NUM_SI_REGISTERS] = {
#define X(reg) #reg,
#include "si/registers.md"
#undef X
};
#endif

static int read_pif_ram(void *opaque, uint32_t address, uint32_t *word);
static int write_pif_ram(void *opaque, uint32_t address, uint32_t word, uint32_t dqm);

static void pif_process(struct si_controller *si);
static int pif_perform_command(struct si_controller *si, unsigned channel,
  uint8_t *send_buf, uint8_t send_bytes, uint8_t *recv_buf, uint8_t recv_bytes);

static int eeprom_read(struct eeprom *eeprom, uint8_t *send_buf, uint8_t send_bytes, uint8_t *recv_buf, uint8_t recv_bytes);
static int eeprom_write(struct eeprom *eeprom, uint8_t *send_buf, uint8_t send_bytes, uint8_t *recv_buf, uint8_t recv_bytes);

// Initializes the SI.
int si_init(struct si_controller *si, struct bus_controller *bus,
  const uint8_t *pif_rom, const uint8_t *cart_rom,
  const struct dd_variant *dd_variant,
  uint8_t *eeprom, size_t eeprom_size,
  const struct controller *controller) {
  uint32_t cic_seed;

  si->bus = bus;
  si->rom = pif_rom;

  if (cart_rom) {
    if (get_cic_seed(cart_rom, &cic_seed)) {
      printf("Unknown CIC type; is this a byte-swapped ROM?\n");
      return 1;
    }

    si->ram[0x24] = cic_seed >> 24;
    si->ram[0x25] = cic_seed >> 16;
    si->ram[0x26] = cic_seed >>  8;
    si->ram[0x27] = cic_seed >>  0;
  }

  else if (dd_variant != NULL) {
    si->ram[0x24] = 0x00;
    si->ram[0x25] = 0x0A;
    si->ram[0x26] = dd_variant->seed; // 0xDD - JP, 0xDE - US
    si->ram[0x27] = 0x3F;
  }

  // Specify 8MiB RDRAM for 6102/6105 carts.
  if (si->ram[0x26] == 0x3F && si->ram[0x27] == 0x3F)
    bus_write_word(si->bus, 0x318, 0x800000, ~0U);

  else if (si->ram[0x26] == 0x91 && si->ram[0x27] == 0x3F)
    bus_write_word(si->bus, 0x3F0, 0x800000, ~0U);

  // initialize EEPROM
  si->eeprom.data = eeprom;
  si->eeprom.size = eeprom_size;

  // initialize RTC
  rtc_init(&si->rtc);

  // controllers
  memcpy(si->controller, controller, sizeof(struct controller) * 4);

  return 0;
}

// Handles a single PIF command.
int pif_perform_command(struct si_controller *si,
  unsigned channel, uint8_t *send_buf, uint8_t send_bytes,
  uint8_t *recv_buf, uint8_t recv_bytes) {
  uint8_t command = send_buf[0];
  struct bus_controller *bus;

  switch(command) {
    // Read status/reset.
    case 0x00:
    case 0xFF:
      switch(channel) {
        case 0:
          // always return that controller 0 is connected so that users
          // who don't specify a controller on command line will still
          // have good experience
          recv_buf[0] = 0x05;
          recv_buf[1] = 0x00;
          recv_buf[2] = si->controller[channel].pak == PAK_NONE ? 0x00 : 0x01;
          break;

        case 1:
        case 2:
        case 3:
          if (si->controller[channel].present) {
            recv_buf[0] = 0x05;
            recv_buf[1] = 0x00;
            recv_buf[2] = si->controller[channel].pak == PAK_NONE ? 0x00 : 0x01;
          }
          else {
            recv_buf[0] = recv_buf[1] = recv_buf[2] = 0xFF;
            return 1;
          }
          break;

        case 4:
          switch (si->eeprom.size) {
            case 0x200: // 4 kbit EEPROM
              recv_buf[0] = 0x00;
              recv_buf[1] = 0x80;
              recv_buf[2] = 0x00;
              break;
            case 0x800: // 16 kbit EEPROM
              recv_buf[0] = 0x00;
              recv_buf[1] = 0xC0;
              recv_buf[2] = 0x00;
              break;
          }
          break;

        default:
          assert(0 && "Invalid channel.");
          return 1;
      }

      break;

    // Read from controller.
    case 0x01:
      switch(channel) {
        case 0:
          memcpy(&bus, si, sizeof(bus));

          if (likely(bus->vi->window)) {
            cen64_mutex_lock(&bus->vi->window->event_mutex);
            memcpy(recv_buf, si->input, sizeof(si->input));
            cen64_mutex_unlock(&bus->vi->window->event_mutex);
          }

          // -nointerface
          else
            memset(recv_buf, 0x0, sizeof(si->input));

          break;

        default:
          return 1;
      }

      break;

    // Read from controller pak
    case 0x02:
      if (channel > 3) {
        assert(0 && "Invalid channel for controller pak read");
        return 1;
      }
      return controller_pak_read(&si->controller[channel],
        send_buf, send_bytes, recv_buf, recv_bytes);

    // Write to controller pak
    case 0x03:
      if (channel > 3) {
        assert(0 && "Invalid channel for controller pak write");
        return 1;
      }
      return controller_pak_write(&si->controller[channel],
        send_buf, send_bytes, recv_buf, recv_bytes);

    // EEPROM read
    case 0x04:
      if (channel != 4) {
        assert(0 && "Invalid channel for EEPROM read");
        return 1;
      }
      return eeprom_read(&si->eeprom, send_buf, send_bytes, recv_buf, recv_bytes);

    // EEPROM write
    case 0x05:
      if (channel != 4) {
        assert(0 && "Invalid channel for EEPROM write");
        return 1;
      }
      return eeprom_write(&si->eeprom, send_buf, send_bytes, recv_buf, recv_bytes);

    // RTC status
    case 0x06:
      if (channel != 4) {
        assert(0 && "Invalid channel for RTC status");
        return 1;
      }
      return rtc_status(&si->rtc, send_buf, send_bytes, recv_buf, recv_bytes);

    // RTC read
    case 0x07:
      if (channel != 4) {
        assert(0 && "Invalid channel for RTC read");
        return 1;
      }
      return rtc_read(&si->rtc, send_buf, send_bytes, recv_buf, recv_bytes);

    // RTC write
    case 0x08:
      if (channel != 4) {
        assert(0 && "Invalid channel for RTC write");
        return 1;
      }
      return rtc_write(&si->rtc, send_buf, send_bytes, recv_buf, recv_bytes);

    // Unimplemented command:
    default:
      return 1;
  }

  return 0;
}

// Emulates the PIF operation.
void pif_process(struct si_controller *si) {
  unsigned channel = 0;
  unsigned ptr = 0;

  if (si->command[0x3F] != 0x1)
    return;

  // Logic ripped from MAME.
  while (ptr < 0x3F) {
    int8_t send_bytes = si->command[ptr++];

    if (send_bytes == -2)
      break;

    if (send_bytes < 0)
      continue;

    if (send_bytes > 0 && (send_bytes & 0xC0) == 0) {
      int8_t recv_bytes = si->command[ptr++];
      uint8_t recv_buf[0x40];
      uint8_t send_buf[0x40];

      if (recv_bytes == -2)
        break;

      // SECURITY: Ensure memcpy cannot buffer overflow
      // if send_bytes or recv_bytes exceed si->command.
      if (
        (ptr + send_bytes) > sizeof(si->command) ||
        (ptr + send_bytes + recv_bytes) > sizeof(si->command)
      )
        break;

      memcpy(send_buf, si->command + ptr, send_bytes);
      ptr += send_bytes;
      memcpy(recv_buf, si->command + ptr, recv_bytes);

      int result = pif_perform_command(si, channel,
        send_buf, send_bytes, recv_buf, recv_bytes);

      if (result == 0) {
        memcpy(si->ram + ptr, recv_buf, recv_bytes);
        ptr += recv_bytes;
      }

      else
        si->ram[ptr - 2] |= 0x80;
    }

    channel++;
  }

  // clear the PIF's busy flag
  si->ram[0x3F] &= ~0x80;
}

// Reads a word from PIF RAM.
int read_pif_ram(void *opaque, uint32_t address, uint32_t *word) {
  struct si_controller *si = (struct si_controller *) opaque;
  unsigned offset = (address - PIF_RAM_BASE_ADDRESS) & 0x3F;

  if (offset == 0x24) {
    si->pif_status = 0x80;

    memcpy(word, si->ram + offset, sizeof(*word));
    *word = byteswap_32(*word);
  }

  else if (offset == 0x3C)
    *word = si->pif_status;

  else {
    memcpy(word, si->ram + offset, sizeof(*word));
    *word = byteswap_32(*word);
  }

  return 0;
}

// Reads a word from PIF ROM.
int read_pif_rom_and_ram(void *opaque, uint32_t address, uint32_t *word) {
  uint32_t offset = address - PIF_ROM_BASE_ADDRESS;
  struct si_controller *si = (struct si_controller*) opaque;

  if (address >= PIF_RAM_BASE_ADDRESS)
    return read_pif_ram(opaque, address, word);

  memcpy(word, si->rom + offset, sizeof(*word));
  *word = byteswap_32(*word);
  return 0;
}

// Reads a word from the SI MMIO register space.
int read_si_regs(void *opaque, uint32_t address, uint32_t *word) {
  struct si_controller *si = (struct si_controller *) opaque;
  unsigned offset = address - SI_REGS_BASE_ADDRESS;
  enum si_register reg = (offset >> 2);

  *word = si->regs[reg];
  debug_mmio_read(si, si_register_mnemonics[reg], *word);
  return 0;
}

// Writes a word to PIF RAM.
int write_pif_ram(void *opaque, uint32_t address, uint32_t word, uint32_t dqm) {
  struct si_controller *si = (struct si_controller *) opaque;
  unsigned offset = (address - PIF_RAM_BASE_ADDRESS) & 0x3F;
  uint32_t orig_word;

  memcpy(&orig_word, si->ram + offset, sizeof(orig_word));
  orig_word = byteswap_32(orig_word) & ~dqm;
  word = byteswap_32(orig_word | word);
  memcpy(si->ram + offset, &word, sizeof(word));

  si->regs[SI_STATUS_REG] |= 0x1000;
  signal_rcp_interrupt(si->bus->vr4300, MI_INTR_SI);
  return 0;
}

// Writes a word to PIF ROM.
int write_pif_rom_and_ram(void *opaque, uint32_t address, uint32_t word, uint32_t dqm) {
  if (address >= PIF_RAM_BASE_ADDRESS)
    return write_pif_ram(opaque, address, word, dqm);
  return 0;
}

// Writes a word to the SI MMIO register space.
int write_si_regs(void *opaque, uint32_t address, uint32_t word, uint32_t dqm) {
  struct si_controller *si = (struct si_controller *) opaque;
  unsigned offset = address - SI_REGS_BASE_ADDRESS;
  enum si_register reg = (offset >> 2);

  debug_mmio_write(si, si_register_mnemonics[reg], word, dqm);

  if (reg == SI_STATUS_REG) {
    clear_rcp_interrupt(si->bus->vr4300, MI_INTR_SI);
    si->regs[SI_STATUS_REG] &= ~0x1000;
  }

  else if (reg == SI_PIF_ADDR_RD64B_REG) {
    uint32_t offset = si->regs[SI_DRAM_ADDR_REG] & 0x1FFFFFFF;

    pif_process(si);
    memcpy(si->bus->ri->ram + offset,
      si->ram, sizeof(si->ram));

    signal_rcp_interrupt(si->bus->vr4300, MI_INTR_SI);
    si->regs[SI_STATUS_REG] |= 0x1000;
  }

  else if (reg == SI_PIF_ADDR_WR64B_REG) {
    uint32_t offset = si->regs[SI_DRAM_ADDR_REG] & 0x1FFFFFFF;

    memcpy(si->ram, si->bus->ri->ram + offset, sizeof(si->ram));
    memcpy(si->command, si->ram, sizeof(si->command));

    signal_rcp_interrupt(si->bus->vr4300, MI_INTR_SI);
    si->regs[SI_STATUS_REG] |= 0x1000;
  }

  else {
    si->regs[reg] &= ~dqm;
    si->regs[reg] |= word;
  }

  return 0;
}


int eeprom_read(struct eeprom *eeprom, uint8_t *send_buf, uint8_t send_bytes, uint8_t *recv_buf, uint8_t recv_bytes) {
  assert(send_bytes == 2 && recv_bytes == 8);

  uint16_t address = send_buf[1] << 3;

  if (eeprom->data != NULL && address <= eeprom->size - 8) {
    memcpy(recv_buf, &eeprom->data[address], 8);
    return 0;
  }

  return 1;
}

static int eeprom_write(struct eeprom *eeprom, uint8_t *send_buf, uint8_t send_bytes, uint8_t *recv_buf, uint8_t recv_bytes) {
  assert(send_bytes == 10);

  uint16_t address = send_buf[1] << 3;

  if (eeprom->data != NULL && address <= eeprom->size - 8) {
    memcpy(&eeprom->data[address], send_buf + 2, 8);
    return 0;
  }

  return 1;
}
