Basic implementation of the Transferpak, is now partially working.

Have to re-implement the RTC and ensure the mbc emulation of all current carts is accurate.
Settings implementation needs to be done as well.

This code is based on work from NRAGE and Bobby Smiles work on Mupen64plus's implementation.
This commit is contained in:
Emmet Young 2016-01-29 22:17:59 +11:00
parent 5f0a77f897
commit ff5fcf6d33
8 changed files with 1036 additions and 4 deletions

View File

@ -0,0 +1,810 @@
/****************************************************************************
* *
* Project64 - A Nintendo 64 emulator. *
* http://www.pj64-emu.com/ *
* Copyright (C) 2012 Project64. All rights reserved. *
* *
* License: *
* GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html *
* *
****************************************************************************/
#include "stdafx.h"
#include "GBCart.h"
#include <Project64-core/N64System/SystemGlobals.h>
//--------------------------------------------------------------------------------------
bool read_from_file(const char *filename, void *data, size_t size)
{
FILE *f = fopen(filename, "rb");
if (f == NULL)
{
return false;
}
if (fread(data, 1, size, f) != size)
{
fclose(f);
return false;
}
fclose(f);
return true;
}
bool load_file(const char* filename, void** buffer, size_t* size)
{
FILE* fd;
size_t l_size;
void* l_buffer;
int err;
bool ret;
/* open file */
ret = false;
fd = fopen(filename, "rb");
if (fd == NULL)
{
return false;
}
/* obtain file size */
ret = false;
err = fseek(fd, 0, SEEK_END);
if (err != 0)
{
goto close_file;
}
err = ftell(fd);
if (err == -1)
{
goto close_file;
}
l_size = (size_t)err;
err = fseek(fd, 0, SEEK_SET);
if (err != 0)
{
goto close_file;
}
/* allocate buffer */
l_buffer = malloc(l_size);
if (l_buffer == NULL)
{
goto close_file;
}
/* copy file content to buffer */
ret = false;
err = fread(l_buffer, 1, l_size, fd);
if (err != l_size)
{
free(l_buffer);
goto close_file;
}
/* commit buffer,size */
ret = true;
*buffer = l_buffer;
*size = l_size;
/* close file */
close_file:
fclose(fd);
return ret;
}
//--------------------------------------------------------------------------------------
static void read_gb_cart_normal(struct gb_cart* gb_cart, uint16_t address, uint8_t* data)
{
uint16_t offset;
if ((address >= 0x0000) && (address <= 0x7FFF))
{
//Read GB Cart
if (address >= gb_cart->rom_size)
{
//If address is larger then our rome size, bail out
return;
}
memcpy(data, &gb_cart->rom[address], 0x20);
}
else if ((address >= 0xA000) && (address <= 0xBFFF))
{
//Read from RAM
if (gb_cart->ram == NULL)
{
//No RAM to write to
return;
}
offset = address - 0xA000;
if (offset >= gb_cart->ram_size)
{
//Offset is larger then our ram size
return;
}
memcpy(&gb_cart->ram[offset], data, 0x20);
memcpy(data, &gb_cart->ram[offset], 0x20);
}
}
static void write_gb_cart_normal(struct gb_cart* gb_cart, uint16_t address, const uint8_t* data)
{
uint16_t offset;
if ((address >= 0xA000) && (address <= 0xBFFF))
{
//Write to RAM
if (gb_cart->ram == NULL)
{
//No RAM to write to
return;
}
offset = address - 0xa000;
if (offset >= gb_cart->ram_size)
{
//Offset is larger then our ram size
return;
}
memcpy(&gb_cart->ram[offset], data, 0x20);
}
}
static void read_gb_cart_mbc1(struct gb_cart* gb_cart, uint16_t address, uint8_t* data)
{
size_t offset;
if ((address >= 0x0000) && (address <= 0x3FFF)) //No nbanked memory
{
memcpy(data, &gb_cart->rom[address], 0x20);
}
else if ((address >= 0x4000) && (address <= 0x7FFF)) //Read from ROM
{
offset = (address - 0x4000) + (gb_cart->rom_bank * 0x4000);
if (offset < gb_cart->rom_size)
{
memcpy(data, &gb_cart->rom[offset], 0x20);
}
}
else if ((address >= 0xA000) && (address <= 0xBFFF)) //Read from RAM
{
if (gb_cart->ram != NULL)
{
offset = (address - 0xA000) + (gb_cart->ram_bank * 0x2000);
if (offset < gb_cart->ram_size)
{
memcpy(data, &gb_cart->ram[offset], 0x20);
}
}
}
}
static void write_gb_cart_mbc1(struct gb_cart* gb_cart, uint16_t address, const uint8_t* data)
{
size_t offset;
if ((address >= 0x0000) && (address <= 0x1FFF)) // RAM enable
{
//Enable/disable RAM
gb_cart->ram_enabled = (data[0] & 0x0F) == 0x0A;
}
else if ((address >= 0x2000) && (address <= 0x3FFF)) // ROM bank select
{
gb_cart->rom_bank &= 0x60; // keep MSB
gb_cart->rom_bank |= data[0] & 0x1F;
// emulate quirk: 0x00 -> 0x01, 0x20 -> 0x21, 0x40->0x41, 0x60 -> 0x61
if ((gb_cart->rom_bank & 0x1F) == 0)
{
gb_cart->rom_bank |= 0x01;
}
}
else if ((address >= 0x4000) && (address <= 0x5FFF)) // RAM bank select
{
if (gb_cart->ram_bank_mode)
{
gb_cart->ram_bank = data[0] & 0x03;
}
else
{
gb_cart->rom_bank &= 0x1F;
gb_cart->rom_bank |= ((data[0] & 0x03) << 5); // set bits 5 and 6 of ROM bank
}
}
else if ((address >= 0x6000) && (address <= 0x7FFF)) // MBC1 mode select
{
// this is overly complicated, but it keeps us from having to do bitwise math later
// Basically we shuffle the 2 "magic bits" between rom_bank and ram_bank as necessary.
if (gb_cart->ram_bank_mode != (data[0] & 0x01))
{
// we should only alter the ROM and RAM bank numbers if we have changed modes
gb_cart->ram_bank_mode = data[0] & 0x01;
if (gb_cart->ram_bank_mode)
{
gb_cart->ram_bank = gb_cart->rom_bank >> 5; // set the ram bank to the "magic bits"
gb_cart->rom_bank &= 0x1F; // zero out bits 5 and 6 to keep consistency
}
else
{
gb_cart->rom_bank &= 0x1F;
gb_cart->rom_bank |= (gb_cart->ram_bank << 5);
gb_cart->ram_bank = 0x00; // we can only reach RAM page 0
}
}
}
else if ((address >= 0xA000) && (address <= 0xBFFF)) // Write to RAM
{
if (gb_cart->ram != NULL)
{
offset = (address - 0xA000) + (gb_cart->ram_bank * 0x2000);
if (offset < gb_cart->ram_size)
{
memcpy(&gb_cart->ram[offset], data, 0x20);
}
}
}
}
static void read_gb_cart_mbc2(struct gb_cart* gb_cart, uint16_t address, uint8_t* data)
{
size_t offset;
if ((address < 0x4000)) //Rom Bank 0
{
memcpy(data, &gb_cart->rom[address], 0x20);
}
else if ((address >= 0x4000) && (address < 0x8000)) //Switchable Rom Bank
{
offset = (address - 0x4000) + (gb_cart->rom_bank * 0x4000);
if (offset < gb_cart->rom_size)
{
memcpy(data, &gb_cart->rom[offset], 0x20);
}
}
else if ((address >= 0xA000) && (address <= 0xC000)) //Upper Bounds of memory map
{
if (gb_cart->ram != NULL)
{
offset = (address - 0xA000) + (gb_cart->ram_bank * 0x2000);
if (offset < gb_cart->ram_size)
{
memcpy(data, &gb_cart->ram[offset], 0x20);
}
}
}
}
static void write_gb_cart_mbc2(struct gb_cart* gb_cart, uint16_t address, const uint8_t* data)
{
size_t offset;
if ((address >= 0x0000) && (address <= 0x1FFF)) // We shouldn't be able to read/write to RAM unless this is toggled on
{
gb_cart->ram_enabled = (data[0] & 0x0F) == 0x0A;
}
else if ((address >= 0x2000) && (address <= 0x3FFF)) // ROM bank select
{
gb_cart->rom_bank = data[0] & 0x0F;
if (gb_cart->rom_bank == 0)
{
gb_cart->rom_bank = 1;
}
}
else if ((address >= 0x4000) && (address <= 0x5FFF)) // RAM bank select
{
if (gb_cart->ram != NULL)
{
gb_cart->ram_bank = data[0] & 0x07;
}
}
else if ((address >= 0xA000) && (address <= 0xBFFF)) // Write to RAM
{
if (gb_cart->ram != NULL)
{
offset = (address - 0xA000) + (gb_cart->ram_bank * 0x2000);
if (offset < gb_cart->ram_size)
{
memcpy(&gb_cart->ram[offset], data, 0x20);
}
}
}
}
static void read_gb_cart_mbc3(struct gb_cart* gb_cart, uint16_t address, uint8_t* data)
{
size_t offset;
if ((address < 0x4000)) //Rom Bank 0
{
memcpy(data, &gb_cart->rom[address], 0x20);
}
else if ((address >= 0x4000) && (address < 0x8000)) //Switchable Rom Bank
{
offset = (address - 0x4000) + (gb_cart->rom_bank * 0x4000);
if (offset < gb_cart->rom_size)
{
memcpy(data, &gb_cart->rom[offset], 0x20);
}
else
{
memset(data, 0x00, 0x20);
}
}
else if ((address >= 0xA000) && (address <= 0xC000)) //Upper Bounds of memory map
{
if (gb_cart->ram != NULL)
{
if (gb_cart->has_rtc && (gb_cart->ram_bank >= 0x08 && gb_cart->ram_bank <= 0x0c))
{
/* XXX: implement RTC read */
memset(data, 0, 0x20);
}
else
{
offset = (address - 0xA000) + (gb_cart->ram_bank * 0x2000);
if (offset < gb_cart->ram_size)
{
memcpy(data, &gb_cart->ram[offset], 0x20);
}
}
}
}
}
static void write_gb_cart_mbc3(struct gb_cart* gb_cart, uint16_t address, const uint8_t* data)
{
uint8_t bank;
size_t offset;
if ((address >= 0x0000) && (address <= 0x1FFF)) // We shouldn't be able to read/write to RAM unless this is toggled on
{
//Enable / Disable RAM -- NOT WORKING -- FIXME
gb_cart->ram_enabled = (data[0] & 0x0F) == 0x0A;
}
else if ((address >= 0x2000) && (address <= 0x3FFF)) // ROM bank select
{
bank = data[0] & 0x7f;
gb_cart->rom_bank = (bank == 0) ? 1 : bank;
}
else if ((address >= 0x4000) && (address <= 0x5FFF)) // RAM/Clock bank select
{
if (gb_cart->ram != NULL)
{
bank = data[0];
if (gb_cart->has_rtc && (bank >= 0x8 && bank <= 0xc))
{
//Set the bank for the timer
gb_cart->ram_bank = bank;
}
else
{
gb_cart->ram_bank = bank & 0x03;
}
}
}
else if ((address >= 0x6000) && (address <= 0x7FFF)) // Latch timer data
{
//Implement RTC timer / latch
}
else if ((address >= 0xA000) && (address <= 0xBFFF)) // Write to RAM
{
if (gb_cart->ram != NULL)
{
if (gb_cart->has_rtc && (gb_cart->ram_bank >= 0x8 && gb_cart->ram_bank <= 0xC))
{
/* XXX: implement RTC write */
}
else
{
offset = (address - 0xA000) + (gb_cart->ram_bank * 0x2000);
if (offset < gb_cart->ram_size)
{
memcpy(&gb_cart->ram[offset], data, 0x20);
}
}
}
}
}
static void read_gb_cart_mbc4(struct gb_cart* gb_cart, uint16_t address, uint8_t* data)
{
g_Notify->BreakPoint(__FILE__, __LINE__);
}
static void write_gb_cart_mbc4(struct gb_cart* gb_cart, uint16_t address, const uint8_t* data)
{
g_Notify->BreakPoint(__FILE__, __LINE__);
}
static void read_gb_cart_mbc5(struct gb_cart* gb_cart, uint16_t address, uint8_t* data)
{
size_t offset;
if ((address < 0x4000)) //Rom Bank 0
{
memcpy(data, &gb_cart->rom[address], 0x20);
}
else if ((address >= 0x4000) && (address < 0x8000)) //Switchable ROM BANK
{
offset = (address - 0x4000) + (gb_cart->rom_bank * 0x4000);
if (offset < gb_cart->rom_size)
{
memcpy(data, &gb_cart->rom[offset], 0x20);
}
}
else if ((address >= 0xA000) && (address <= 0xC000)) //Upper bounds of memory map
{
if (gb_cart->ram != NULL)
{
offset = (address - 0xA000) + (gb_cart->ram_bank * 0x2000);
if (offset < gb_cart->ram_size)
{
memcpy(data, &gb_cart->ram[offset], 0x20);
}
}
}
}
static void write_gb_cart_mbc5(struct gb_cart* gb_cart, uint16_t address, const uint8_t* data)
{
size_t offset;
if ((address >= 0x0000) && (address <= 0x1FFF)) // We shouldn't be able to read/write to RAM unless this is toggled on
{
//Enable / Disable RAM -- NOT WORKING -- CHECK ME
gb_cart->ram_enabled = (data[0] & 0x0F) == 0x0A;
}
else if ((address >= 0x2000) && (address <= 0x2FFF)) // ROM bank select, low bits
{
gb_cart->rom_bank &= 0xff00;
gb_cart->rom_bank |= data[0];
}
else if ((address >= 0x3000) && (address <= 0x3FFF)) // ROM bank select, high bit
{
gb_cart->rom_bank &= 0x00ff;
gb_cart->rom_bank |= (data[0] & 0x01) << 8;
}
else if ((address >= 0x4000) && (address <= 0x5FFF)) // RAM bank select
{
if (gb_cart->ram != NULL)
{
gb_cart->ram_bank = data[0] & 0x0f;
}
}
else if ((address >= 0xA000) && (address <= 0xBFFF)) // Write to RAM
{
if (gb_cart->ram != NULL)
{
offset = (address - 0xA000) + (gb_cart->ram_bank * 0x2000);
if (offset < gb_cart->ram_size)
{
memcpy(&gb_cart->ram[offset], data, 0x20);
}
}
}
}
static void read_gb_cart_mmm01(struct gb_cart* gb_cart, uint16_t address, uint8_t* data)
{
g_Notify->BreakPoint(__FILE__, __LINE__);
}
static void write_gb_cart_mmm01(struct gb_cart* gb_cart, uint16_t address, const uint8_t* data)
{
g_Notify->BreakPoint(__FILE__, __LINE__);
}
static void read_gb_cart_pocket_cam(struct gb_cart* gb_cart, uint16_t address, uint8_t* data)
{
size_t offset;
if ((address < 0x4000)) //Rom Bank 0
{
memcpy(data, &gb_cart->rom[address], 0x20);
}
else if ((address >= 0x4000) && (address < 0x8000)) //Switchable ROM BANK
{
offset = (address - 0x4000) + (gb_cart->rom_bank * 0x4000);
if (offset < gb_cart->rom_size)
{
memcpy(data, &gb_cart->rom[offset], 0x20);
}
}
else if ((address >= 0xA000) && (address <= 0xC000)) //Upper bounds of memory map
{
//Check to see if where currently in register mode
if (gb_cart->ram != NULL)
{
if (gb_cart->ram_bank & 0x10)
{
//Where in register mode, based off NRAGE we just fill the memory with Zeroes.
//Seems to be incorrect behaviour but need to find more doccumentation
memset(data, 0x00, 0x20);
}
else
{
//Read RAM normally
offset = (address - 0xA000) + (gb_cart->ram_bank * 0x2000);
if (offset < gb_cart->ram_size)
{
memcpy(data, &gb_cart->ram[offset], 0x20);
}
}
}
}
}
static void write_gb_cart_pocket_cam(struct gb_cart* gb_cart, uint16_t address, const uint8_t* data)
{
size_t offset;
if ((address >= 0x0000) && (address <= 0x1FFF)) // We shouldn't be able to read/write to RAM unless this is toggled on
{
//Enable / Disable RAM
gb_cart->ram_enabled = (data[0] & 0x0F) == 0x0A;
}
else if ((address >= 0x2000) && (address <= 0x2FFF)) // ROM bank select, low bits
{
gb_cart->rom_bank &= 0xFF00;
gb_cart->rom_bank |= data[0];
}
else if ((address >= 0x4000) && (address <= 0x4FFF)) // Camera Register & RAM bank select
{
if (gb_cart->ram != NULL)
{
if (data[0] & 0x10)
{
//REGISTER MODE
gb_cart->ram_bank = data[0];
}
else
{
//RAM MODE
gb_cart->ram_bank = data[0] & 0x0F;
}
}
}
else if ((address >= 0xA000) && (address <= 0xBFFF)) // Write to RAM
{
if (gb_cart->ram != NULL)
{
if (gb_cart->ram_bank & 0x10)
{
//REGISTER MODE (DO NOTHING)
}
else
{
//RAM MODE
offset = (address - 0xA000) + (gb_cart->ram_bank * 0x2000);
if (offset < gb_cart->ram_size)
{
memcpy(&gb_cart->ram[offset], data, 0x20);
}
}
}
}
}
static void read_gb_cart_bandai_tama5(struct gb_cart* gb_cart, uint16_t address, uint8_t* data)
{
g_Notify->BreakPoint(__FILE__, __LINE__);
}
static void write_gb_cart_bandai_tama5(struct gb_cart* gb_cart, uint16_t address, const uint8_t* data)
{
g_Notify->BreakPoint(__FILE__, __LINE__);
}
static void read_gb_cart_huc1(struct gb_cart* gb_cart, uint16_t address, uint8_t* data)
{
g_Notify->BreakPoint(__FILE__, __LINE__);
}
static void write_gb_cart_huc1(struct gb_cart* gb_cart, uint16_t address, const uint8_t* data)
{
g_Notify->BreakPoint(__FILE__, __LINE__);
}
static void read_gb_cart_huc3(struct gb_cart* gb_cart, uint16_t address, uint8_t* data)
{
g_Notify->BreakPoint(__FILE__, __LINE__);
}
static void write_gb_cart_huc3(struct gb_cart* gb_cart, uint16_t address, const uint8_t* data)
{
g_Notify->BreakPoint(__FILE__, __LINE__);
}
enum gbcart_extra_devices
{
GED_NONE = 0x00,
GED_RAM = 0x01,
GED_BATTERY = 0x02,
GED_RTC = 0x04,
GED_RUMBLE = 0x08
};
struct parsed_cart_type
{
void(*read_gb_cart)(struct gb_cart*, uint16_t, uint8_t*);
void(*write_gb_cart)(struct gb_cart*, uint16_t, const uint8_t*);
unsigned int extra_devices;
};
static const struct parsed_cart_type* parse_cart_type(uint8_t cart_type)
{
#define MBC(x) read_gb_cart_ ## x, write_gb_cart_ ## x
static const struct parsed_cart_type GB_CART_TYPES[] =
{
{ MBC(normal), GED_NONE },
{ MBC(mbc1), GED_NONE },
{ MBC(mbc1), GED_RAM },
{ MBC(mbc1), GED_RAM | GED_BATTERY },
{ MBC(mbc2), GED_NONE },
{ MBC(mbc2), GED_BATTERY },
{ MBC(normal), GED_RAM },
{ MBC(normal), GED_RAM | GED_BATTERY },
{ MBC(mmm01), GED_NONE },
{ MBC(mmm01), GED_RAM },
{ MBC(mmm01), GED_RAM | GED_BATTERY },
{ MBC(mbc3), GED_BATTERY | GED_RTC },
{ MBC(mbc3), GED_RAM | GED_BATTERY | GED_RTC },
{ MBC(mbc3), GED_NONE },
{ MBC(mbc3), GED_RAM },
{ MBC(mbc3), GED_RAM | GED_BATTERY },
{ MBC(mbc4), GED_NONE },
{ MBC(mbc4), GED_RAM },
{ MBC(mbc4), GED_RAM | GED_BATTERY },
{ MBC(mbc5), GED_NONE },
{ MBC(mbc5), GED_RAM },
{ MBC(mbc5), GED_RAM | GED_BATTERY },
{ MBC(mbc5), GED_RUMBLE },
{ MBC(mbc5), GED_RAM | GED_RUMBLE },
{ MBC(mbc5), GED_RAM | GED_BATTERY | GED_RUMBLE },
{ MBC(pocket_cam), GED_NONE },
{ MBC(bandai_tama5), GED_NONE },
{ MBC(huc3), GED_NONE },
{ MBC(huc1), GED_RAM | GED_BATTERY }
};
#undef MBC
switch (cart_type)
{
case 0x00: return &GB_CART_TYPES[0];
case 0x01: return &GB_CART_TYPES[1];
case 0x02: return &GB_CART_TYPES[2];
case 0x03: return &GB_CART_TYPES[3];
case 0x05: return &GB_CART_TYPES[4];
case 0x06: return &GB_CART_TYPES[5];
case 0x08: return &GB_CART_TYPES[6];
case 0x09: return &GB_CART_TYPES[7];
case 0x0b: return &GB_CART_TYPES[8];
case 0x0c: return &GB_CART_TYPES[9];
case 0x0d: return &GB_CART_TYPES[10];
case 0x0f: return &GB_CART_TYPES[11];
case 0x10: return &GB_CART_TYPES[12];
case 0x11: return &GB_CART_TYPES[13];
case 0x12: return &GB_CART_TYPES[14];
case 0x13: return &GB_CART_TYPES[15];
case 0x15: return &GB_CART_TYPES[16];
case 0x16: return &GB_CART_TYPES[17];
case 0x17: return &GB_CART_TYPES[18];
case 0x19: return &GB_CART_TYPES[19];
case 0x1a: return &GB_CART_TYPES[20];
case 0x1b: return &GB_CART_TYPES[21];
case 0x1c: return &GB_CART_TYPES[22];
case 0x1d: return &GB_CART_TYPES[23];
case 0x1e: return &GB_CART_TYPES[24];
case 0xfc: return &GB_CART_TYPES[25];
case 0xfd: return &GB_CART_TYPES[26];
case 0xfe: return &GB_CART_TYPES[27];
case 0xff: return &GB_CART_TYPES[28];
default: return NULL;
}
}
int GBCart::init_gb_cart(struct gb_cart* gb_cart, const char* gb_file)
{
int err;
const struct parsed_cart_type* type;
uint8_t* rom = NULL;
size_t rom_size = 0;
uint8_t* ram = NULL;
size_t ram_size = 0;
/* load GB cart ROM */
err = load_file(gb_file, (void**)&rom, &rom_size);
if (err == 0)
{
return err;
}
if (rom_size < 0x8000)
{
err = 0;
goto free_rom;
}
/* get and parse cart type */
uint8_t cart_type = rom[0x147];
type = parse_cart_type(cart_type);
if (type == NULL)
{
err = 0;
goto free_rom;
}
/* load ram (if present) */
if (type->extra_devices & GED_RAM)
{
ram_size = 0;
switch (rom[0x149])
{
case 0x01: ram_size = 1 * 0x800; break;
case 0x02: ram_size = 4 * 0x800; break;
case 0x03: ram_size = 16 * 0x800; break;
case 0x04: ram_size = 64 * 0x800; break;
case 0x05: ram_size = 32 * 0x800; break;
}
if (ram_size != 0)
{
ram = (uint8_t*)malloc(ram_size);
if (ram == NULL)
{
err = 0;
goto free_rom;
}
read_from_file("C:/Users/death/Desktop/pkmgb.sav", ram, ram_size);
}
}
/* update gb_cart */
gb_cart->ram = ram;
gb_cart->rom = rom;
gb_cart->rom_size = rom_size;
gb_cart->ram_size = ram_size;
gb_cart->rom_bank = 1;
gb_cart->ram_bank = 0;
gb_cart->has_rtc = (type->extra_devices & GED_RTC) ? 1 : 0;
gb_cart->read_gb_cart = type->read_gb_cart;
gb_cart->write_gb_cart = type->write_gb_cart;
return 1;
free_rom:
free(rom);
return err;
}
void GBCart::release_gb_cart(struct gb_cart* gb_cart)
{
if (gb_cart->rom != NULL)
free(gb_cart->rom);
if (gb_cart->ram != NULL)
free(gb_cart->ram);
memset(gb_cart, 0, sizeof(*gb_cart));
}
void GBCart::read_gb_cart(struct gb_cart* gb_cart, uint16_t address, uint8_t* data)
{
gb_cart->read_gb_cart(gb_cart, address, data);
}
void GBCart::write_gb_cart(struct gb_cart* gb_cart, uint16_t address, const uint8_t* data)
{
gb_cart->write_gb_cart(gb_cart, address, data);
}

View File

@ -0,0 +1,46 @@
/****************************************************************************
* *
* Project64 - A Nintendo 64 emulator. *
* http://www.pj64-emu.com/ *
* Copyright (C) 2012 Project64. All rights reserved. *
* *
* License: *
* GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html *
* *
****************************************************************************/
#pragma once
#include <stddef.h>
#include <stdint.h>
struct gb_cart
{
uint8_t* rom;
uint8_t* ram;
size_t rom_size;
size_t ram_size;
unsigned int rom_bank;
unsigned int ram_bank;
bool has_rtc;
bool ram_bank_mode;
bool ram_enabled;
void(*read_gb_cart)(struct gb_cart* gb_cart, uint16_t address, uint8_t* data);
void(*write_gb_cart)(struct gb_cart* gb_cart, uint16_t address, const uint8_t* data);
};
class GBCart
{
public:
static int init_gb_cart(struct gb_cart* gb_cart, const char* gb_file);
static void release_gb_cart(struct gb_cart* gb_cart);
static void read_gb_cart(struct gb_cart* gb_cart, uint16_t address, uint8_t* data);
static void write_gb_cart(struct gb_cart* gb_cart, uint16_t address, const uint8_t* data);
};

View File

@ -17,6 +17,7 @@
#include <Project64-core/N64System/Mips/RegisterClass.h>
#include <Project64-core/N64System/Mips/MemoryVirtualMem.h>
#include <Project64-core/N64System/N64Class.h>
#include <Project64-core/N64System/Mips/Transferpak.h>
#include <Project64-core/N64System/Mips/Rumblepak.h>
#include <Project64-core/N64System/Mips/Mempak.H>
#include <Project64-core/Logging.h>
@ -526,7 +527,7 @@ void CPifRam::ProcessControllerCommand(int32_t Control, uint8_t * Command)
{
case PLUGIN_RUMBLE_PAK: Rumblepak::ReadFrom(address, data); break;
case PLUGIN_MEMPAK: Mempak::ReadFrom(Control, address, data); break;
case PLUGIN_TANSFER_PAK: /* TODO */; break;
case PLUGIN_TANSFER_PAK: Transferpak::ReadFrom(address, data); break;
case PLUGIN_RAW: if (g_Plugins->Control()->ControllerCommand) { g_Plugins->Control()->ControllerCommand(Control, Command); } break;
default:
memset(&Command[5], 0, 0x20);
@ -534,7 +535,7 @@ void CPifRam::ProcessControllerCommand(int32_t Control, uint8_t * Command)
if (Controllers[Control].Plugin != PLUGIN_RAW)
{
Command[0x25] = Mempak::CalculateCrc(&Command[5]);
Command[0x25] = Mempak::CalculateCrc(data);
}
}
else
@ -567,13 +568,13 @@ void CPifRam::ProcessControllerCommand(int32_t Control, uint8_t * Command)
{
case PLUGIN_MEMPAK: Mempak::WriteTo(Control, address, data); break;
case PLUGIN_RUMBLE_PAK: Rumblepak::WriteTo(Control, address, data); break;
case PLUGIN_TANSFER_PAK: /* TODO */; break;
case PLUGIN_TANSFER_PAK: Transferpak::WriteTo(address, data); break;
case PLUGIN_RAW: if (g_Plugins->Control()->ControllerCommand) { g_Plugins->Control()->ControllerCommand(Control, Command); } break;
}
if (Controllers[Control].Plugin != PLUGIN_RAW)
{
Command[0x25] = Mempak::CalculateCrc(&Command[5]);
Command[0x25] = Mempak::CalculateCrc(data);
}
}
else

View File

@ -0,0 +1,37 @@
/****************************************************************************
* *
* Project64 - A Nintendo 64 emulator. *
* http://www.pj64-emu.com/ *
* Copyright (C) 2012 Project64. All rights reserved. *
* *
* License: *
* GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html *
* *
****************************************************************************/
#pragma once
#include "GBCart.h"
enum cart_access_mode
{
CART_NOT_INSERTED = 0x40,
CART_ACCESS_MODE_0 = 0x80,
CART_ACCESS_MODE_1 = 0x89
};
struct transferpak
{
unsigned int enabled;
unsigned int bank;
unsigned int access_mode;
unsigned int access_mode_changed;
struct gb_cart gb_cart;
};
class Transferpak
{
public:
static void Release();
static void Init();
static void ReadFrom(uint16_t address, uint8_t * command);
static void WriteTo(uint16_t address, uint8_t * command);
};

View File

@ -0,0 +1,119 @@
/****************************************************************************
* *
* Project64 - A Nintendo 64 emulator. *
* http://www.pj64-emu.com/ *
* Copyright (C) 2012 Project64. All rights reserved. *
* *
* License: *
* GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html *
* *
****************************************************************************/
#include "stdafx.h"
#include "GBCart.h"
#include "Transferpak.h"
static transferpak tpak;
uint16_t gb_cart_address(unsigned int bank, uint16_t address)
{
return (address & 0x3FFF) | ((bank & 0x3) * 0x4000);
}
void Transferpak::Init()
{
memset(&tpak, 0, sizeof(tpak));
tpak.access_mode = (GBCart::init_gb_cart(&tpak.gb_cart, "C:/Users/death/Desktop/pkmgb.gb") == 0) ? CART_NOT_INSERTED : CART_ACCESS_MODE_0;
tpak.access_mode_changed = 0x44;
}
void Transferpak::Release()
{
GBCart::release_gb_cart(&tpak.gb_cart);
}
void Transferpak::ReadFrom(uint16_t address, uint8_t * data)
{
if ((address >= 0x8000) && (address <= 0x8FFF))
{
//Get whether the GB cart is enabled or disabled
uint8_t value = (tpak.enabled) ? 0x84 : 0x00;
memset(data, value, 0x20);
}
else if ((address >= 0xB000) && (address <= 0xBFFF))
{
// Get the GB Cart access mode
if (tpak.enabled)
{
memset(data, tpak.access_mode, 0x20);
if (tpak.access_mode != CART_NOT_INSERTED)
{
data[0] |= tpak.access_mode_changed;
}
tpak.access_mode_changed = 0;
}
}
else if (address >= 0xC000)
{
// Read the GB Cart
if (tpak.enabled)
{
GBCart::read_gb_cart(&tpak.gb_cart, gb_cart_address(tpak.bank, address), data);
}
}
}
void Transferpak::WriteTo(uint16_t address, uint8_t * data)
{
if ((address >= 0x8000) && (address <= 0x8FFF))
{
//Set whether the gb cart is enabled or disabled.
switch (*data)
{
case 0xFE:
tpak.enabled = false;
break;
case 0x84:
tpak.enabled = true;
break;
default:
//Do nothing
break;
}
}
else if ((address >= 0xA000) && (address <= 0xAFFF))
{
//Set the bank for the GB Cart
if (tpak.enabled)
{
tpak.bank = *data;
}
}
else if ((address >= 0xB000) && (address <= 0xBFFF))
{
// Get the GB Cart access mode
if (tpak.enabled)
{
tpak.access_mode_changed = 0x04;
tpak.access_mode = ((*data & 1) == 0) ? CART_ACCESS_MODE_0 : CART_ACCESS_MODE_1;
if ((*data & 0xFE) != 0)
{
//Unkown tpak write
}
}
}
else if (address >= 0xC000)
{
// Write the GB Cart
if (tpak.enabled)
{
GBCart::write_gb_cart(&tpak.gb_cart, gb_cart_address(tpak.bank, address), data);
//Write the GB CART
}
}
}

View File

@ -14,6 +14,7 @@
#include <Project64-core/N64System/Recompiler/x86CodeLog.h>
#include <Project64-core/N64System/SystemGlobals.h>
#include <Project64-core/N64System/Mips/Mempak.H>
#include <Project64-core/N64System/Mips/Transferpak.h>
#include <Project64-core/N64System/Interpreter/InterpreterCPU.h>
#include <Project64-core/N64System/Mips/OpcodeName.h>
#include <Project64-core/N64System/N64DiskClass.h>
@ -66,11 +67,13 @@ m_CheatsSlectionChanged(false)
g_Settings->SaveDword(GameRunning_ScreenHertz, gameHertz);
m_Cheats.LoadCheats(!g_Settings->LoadDword(Setting_RememberCheats), Plugins);
Mempak::Load();
Transferpak::Init();
}
CN64System::~CN64System()
{
SetActiveSystem(false);
Transferpak::Release();
if (m_SyncCPU)
{
m_SyncCPU->CpuStopped();

View File

@ -50,6 +50,7 @@
<ClCompile Include="N64System\Mips\Dma.cpp" />
<ClCompile Include="N64System\Mips\Eeprom.cpp" />
<ClCompile Include="N64System\Mips\FlashRam.cpp" />
<ClCompile Include="N64System\Mips\GBCart.cpp" />
<ClCompile Include="N64System\Mips\MemoryVirtualMem.cpp" />
<ClCompile Include="N64System\Mips\Mempak.cpp" />
<ClCompile Include="N64System\Mips\OpcodeName.cpp" />
@ -60,6 +61,7 @@
<ClCompile Include="N64System\Mips\SystemEvents.cpp" />
<ClCompile Include="N64System\Mips\SystemTiming.cpp" />
<ClCompile Include="N64System\Mips\TLBclass.cpp" />
<ClCompile Include="N64System\Mips\Trasferpak.cpp" />
<ClCompile Include="N64System\N64Class.cpp" />
<ClCompile Include="N64System\N64DiskClass.cpp" />
<ClCompile Include="N64System\N64RomClass.cpp" />
@ -135,6 +137,7 @@
<ClInclude Include="N64System\Mips\Dma.h" />
<ClInclude Include="N64System\Mips\Eeprom.h" />
<ClInclude Include="N64System\Mips\FlashRam.h" />
<ClInclude Include="N64System\Mips\GBCart.h" />
<ClInclude Include="N64System\Mips\MemoryClass.h" />
<ClInclude Include="N64System\Mips\MemoryVirtualMem.h" />
<ClInclude Include="N64System\Mips\Mempak.h" />
@ -147,6 +150,7 @@
<ClInclude Include="N64System\Mips\SystemEvents.h" />
<ClInclude Include="N64System\Mips\SystemTiming.h" />
<ClInclude Include="N64System\Mips\TLBClass.h" />
<ClInclude Include="N64System\Mips\Transferpak.h" />
<ClInclude Include="N64System\Mips\TranslateVaddr.h" />
<ClInclude Include="N64System\N64Class.h" />
<ClInclude Include="N64System\N64DiskClass.h" />

View File

@ -300,6 +300,12 @@
<ClCompile Include="N64System\N64DiskClass.cpp">
<Filter>Source Files\N64 System</Filter>
</ClCompile>
<ClCompile Include="N64System\Mips\GBCart.cpp">
<Filter>Source Files\N64 System\Mips</Filter>
</ClCompile>
<ClCompile Include="N64System\Mips\Trasferpak.cpp">
<Filter>Source Files\N64 System\Mips</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="stdafx.h">
@ -584,5 +590,11 @@
<ClInclude Include="N64System\N64DiskClass.h">
<Filter>Header Files\N64 System</Filter>
</ClInclude>
<ClInclude Include="N64System\Mips\Transferpak.h">
<Filter>Header Files\N64 System\Mips</Filter>
</ClInclude>
<ClInclude Include="N64System\Mips\GBCart.h">
<Filter>Header Files\N64 System\Mips</Filter>
</ClInclude>
</ItemGroup>
</Project>