//
// si/pak_transfer.c: Controller pak routines
//
// CEN64: Cycle-Accurate Nintendo 64 Emulator.
// Copyright (C) 2016, Mike Ryan.
//
// This file is subject to the terms and conditions defined in
// 'LICENSE', which is part of this source code package.
//

#include "pak.h"
#include "gb.h"

static void gameboy_read(struct controller *controller, uint16_t address,
    uint8_t *buffer);
static void gameboy_write(struct controller *controller, uint16_t address,
    uint8_t *buffer);

void transfer_pak_read(struct controller *controller,
    uint8_t *send_buf, uint8_t send_bytes,
    uint8_t *recv_buf, uint8_t recv_bytes) {

  uint16_t address = send_buf[1] << 8 | send_buf[2];
  address &= ~0x1F; // lower 5 bits are address CRC
  // printf("read from %04x\n", address);

  // get enable/disable state
  if (address == 0x8000)
    memset(recv_buf, controller->pak_enabled ? 0x84 : 0x00, 0x20);

  // get insertion status and access mode
  else if (address == 0xB000) {
    if (controller->pak_enabled) {
      // cart inserted, return mode and mode changed
      if (controller->tpak_rom.ptr != NULL) {
        memset(recv_buf, controller->tpak_mode == 1 ? 0x89 : 0x80, 0x20);
        recv_buf[0] |= controller->tpak_mode_changed ? 0x4 : 0;
      }

      // cart not inserted
      else
        memset(recv_buf, 0x40, 0x20);

      controller->tpak_mode_changed = 0;
    }
  }

  // read from GB cart
  else if (address >= 0xC000) {
    if (controller->pak_enabled) {
      uint16_t gb_addr = address - 0xC000
        + (controller->tpak_bank & 3) * 0x4000;
      gameboy_read(controller, gb_addr, recv_buf);
    }
  }
}

void transfer_pak_write(struct controller *controller,
    uint8_t *send_buf, uint8_t send_bytes,
    uint8_t *recv_buf, uint8_t recv_bytes) {

  uint16_t address = send_buf[1] << 8 | send_buf[2];
  address &= ~0x1F; // lower 5 bits are address CRC
  // printf("write to %04x\n", address);

  // set bank
  if (address == 0xA000) {
    if (controller->pak_enabled)
      controller->tpak_bank = send_buf[3];
  }

  // set access mode
  else if (address == 0xB000) {
    if (controller->pak_enabled) {
      controller->tpak_mode = send_buf[3] & 1;
      controller->tpak_mode_changed = 1;
    }
  }

  // write to GB cart
  else if (address >= 0xC000) {
    uint16_t gb_addr = address - 0xC000
      + (controller->tpak_bank & 3) * 0x4000;
    gameboy_write(controller, gb_addr, send_buf + 3);
  }
}

// read 0x20 bytes from Game Boy cart at address
void gameboy_read(struct controller *controller, uint16_t address,
    uint8_t *buffer) {
  for(int i=0;i<0x20;i++)
    buffer[i] = gb_read(controller, address+i);
  
//   printf("read:  %04X: ", address);
//   for(int i=0;i<32;i++) {
//     if(i==16) printf("\n             ");
//     printf("%02X ", buffer[i]);
//   }
//   printf("\n");
}

// write 0x20 bytes from buffer to Game Boy cart at address
void gameboy_write(struct controller *controller, uint16_t address,
    uint8_t *buffer) {
  for(int i=0;i<0x20;i++)
    gb_write(controller, address+i, buffer[i]);
//   printf("write: %04X:%02X\n", address, buffer[0]);
}
