diff --git a/CMakeLists.txt b/CMakeLists.txt index 15cc954..c28f2d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -339,6 +339,7 @@ set(SI_SOURCES ${PROJECT_SOURCE_DIR}/si/cic.c ${PROJECT_SOURCE_DIR}/si/controller.c ${PROJECT_SOURCE_DIR}/si/pak.c + ${PROJECT_SOURCE_DIR}/si/pak_transfer.c ) set(VI_SOURCES diff --git a/si/pak.c b/si/pak.c index 1cb6c67..da16b5b 100644 --- a/si/pak.c +++ b/si/pak.c @@ -9,6 +9,7 @@ // #include "pak.h" +#include "pak_transfer.h" static uint8_t controller_pak_crc(uint8_t *data); @@ -27,12 +28,13 @@ int controller_pak_read(struct controller *controller, assert(0 && "invalid mempak address"); } + else if (controller->pak == PAK_TRANSFER) + transfer_pak_read(controller, send_buf, send_bytes, recv_buf, recv_bytes); + else { uint8_t peripheral = 0x00; if (controller->pak == PAK_RUMBLE) peripheral = 0x80; - else if (controller->pak == PAK_TRANSFER) - peripheral = 0x84; memset(recv_buf, peripheral, 0x20); } @@ -49,8 +51,13 @@ int controller_pak_write(struct controller *controller, address &= ~0b11111; // lower 5 bits are a checksum // printf("write to %04x\n", address); - if (address == 0x8000) - ; // initializing device, do nothing + if (address == 0x8000) { + // maybe only for transfer pak, unclear + if (send_buf[3] == 0xfe) + controller->pak_enabled = 0; + else if (send_buf[3] == 0x84) + controller->pak_enabled = 1; + } else if (controller->pak == PAK_MEM) { if (address <= MEMPAK_SIZE - 0x20) @@ -70,6 +77,9 @@ int controller_pak_write(struct controller *controller, ; // printf("Unknown rumble address\n"); } + else if (controller->pak == PAK_TRANSFER) + transfer_pak_write(controller, send_buf, send_bytes, recv_buf, recv_bytes); + recv_buf[0] = controller_pak_crc(send_buf+3); return 0; diff --git a/si/pak_transfer.c b/si/pak_transfer.c new file mode 100644 index 0000000..13c84f5 --- /dev/null +++ b/si/pak_transfer.c @@ -0,0 +1,99 @@ +// +// si/pak.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" + +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 &= ~0b11111; // 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 ? 0b100 : 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 &= ~0b11111; // 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); + } +} + +// read 0x20 bytes from Game Boy cart at address +void gameboy_read(struct controller *controller, uint16_t address, + uint8_t *buffer) { + // TODO handle mappers + if (address < 0x8000) + memcpy(buffer, controller->tpak_rom.ptr + address, 0x20); +} + +// write 0x20 bytes from buffer to Game Boy cart at address +void gameboy_write(struct controller *controller, uint16_t address, + uint8_t *buffer) { + // TODO +} diff --git a/si/pak_transfer.h b/si/pak_transfer.h new file mode 100644 index 0000000..edfe157 --- /dev/null +++ b/si/pak_transfer.h @@ -0,0 +1,22 @@ +// +// si/pak_transfer.h: Transfer pak handling +// +// 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. +// + +#ifndef __si_pak_transfer_h__ +#define __si_pak_transfer_h__ +#include "common.h" + +void transfer_pak_read(struct controller *controller, + uint8_t *send_buf, uint8_t send_bytes, + uint8_t *recv_buf, uint8_t recv_bytes); +void transfer_pak_write(struct controller *controller, + uint8_t *send_buf, uint8_t send_bytes, + uint8_t *recv_buf, uint8_t recv_bytes); + +#endif