Files
simple64/mupen64plus-core/src/device/gb/gb_cart.c
T
Logan McNaughton bb06cf187e update core
2024-11-10 20:23:42 +01:00

1120 lines
37 KiB
C

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Mupen64plus - gb_cart.c *
* Mupen64Plus homepage: https://mupen64plus.org/ *
* Copyright (C) 2015 Bobby Smiles *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Most of the mappers information comes from
* "The Cycle-Accurate Game Boy Docs" by AntonioND
*/
#include "gb_cart.h"
#include "api/m64p_types.h"
#include "api/callbacks.h"
#include "backends/api/rumble_backend.h"
#include "backends/api/storage_backend.h"
#include "backends/api/video_capture_backend.h"
#if 0
void cv_imshow(const char* name, unsigned int width, unsigned int height, int channels, void* data);
#else
#define cv_imshow(...)
#endif
#include <assert.h>
#include <string.h>
enum gbcart_extra_devices
{
GED_NONE = 0x00,
GED_RAM = 0x01,
GED_BATTERY = 0x02,
GED_RTC = 0x04,
GED_RUMBLE = 0x08,
GED_ACCELEROMETER = 0x10,
GED_CAMERA = 0x20,
};
/* various helper functions for ram, rom, or MBC uses */
static void read_rom(const void* rom_storage, const struct storage_backend_interface* irom_storage, uint16_t address, uint8_t* data, size_t size)
{
assert(size > 0);
if (address + size > irom_storage->size(rom_storage))
{
DebugMessage(M64MSG_WARNING, "Out of bound read from GB ROM %04x", address);
return;
}
memcpy(data, irom_storage->data(rom_storage) + address, size);
}
static void read_ram(const void* ram_storage, const struct storage_backend_interface* iram_storage, unsigned int enabled, uint16_t address, uint8_t* data, size_t size, uint8_t mask)
{
size_t i;
assert(size > 0);
/* RAM has to be enabled before use */
if (!enabled) {
DebugMessage(M64MSG_WARNING, "Trying to read from non enabled GB RAM %04x", address);
memset(data, 0xff, size);
return;
}
/* RAM must be present */
if (iram_storage->data(ram_storage) == NULL) {
DebugMessage(M64MSG_WARNING, "Trying to read from absent GB RAM %04x", address);
memset(data, 0xff, size);
return;
}
if (address + size > iram_storage->size(ram_storage))
{
DebugMessage(M64MSG_WARNING, "Out of bound read from GB RAM %04x", address);
return;
}
memcpy(data, iram_storage->data(ram_storage) + address, size);
if (mask != UINT8_C(0xff)) {
for (i = 0; i < size; ++i) {
data[i] &= mask;
}
}
}
static void write_ram(void* ram_storage, const struct storage_backend_interface* iram_storage, unsigned int enabled, uint16_t address, const uint8_t* data, size_t size, uint8_t mask)
{
size_t i;
uint8_t* dst;
assert(size > 0);
/* RAM has to be enabled before use */
if (!enabled) {
DebugMessage(M64MSG_WARNING, "Trying to write to non enabled GB RAM %04x", address);
return;
}
/* RAM must be present */
if (iram_storage->data(ram_storage) == NULL) {
DebugMessage(M64MSG_WARNING, "Trying to write to absent GB RAM %04x", address);
return;
}
if (address + size > iram_storage->size(ram_storage))
{
DebugMessage(M64MSG_WARNING, "Out of bound write to GB RAM %04x", address);
return;
}
dst = iram_storage->data(ram_storage) + address;
memcpy(dst, data, size);
if (mask != UINT8_C(0xff)) {
for (i = 0; i < size; ++i) {
dst[i] &= mask;
}
}
iram_storage->save(ram_storage, address, size);
}
static void set_ram_enable(struct gb_cart* gb_cart, uint8_t value)
{
gb_cart->ram_enable = ((value & 0x0f) == 0x0a) ? 1 : 0;
DebugMessage(M64MSG_VERBOSE, "RAM enable = %02x", gb_cart->ram_enable);
}
static int read_gb_cart_nombc(struct gb_cart* gb_cart, uint16_t address, uint8_t* data, size_t size)
{
switch(address >> 13)
{
/* 0x0000-0x7fff: ROM */
case (0x0000 >> 13):
case (0x2000 >> 13):
case (0x4000 >> 13):
case (0x6000 >> 13):
read_rom(gb_cart->rom_storage, gb_cart->irom_storage, address, data, size);
break;
/* 0xa000-0xbfff: RAM */
case (0xa000 >> 13):
read_ram(gb_cart->ram_storage, gb_cart->iram_storage, 1, address - 0xa000, data, size, UINT8_C(0xff));
break;
default:
DebugMessage(M64MSG_WARNING, "Invalid cart read (nombc): %04x", address);
}
return 0;
}
static int write_gb_cart_nombc(struct gb_cart* gb_cart, uint16_t address, const uint8_t* data, size_t size)
{
switch(address >> 13)
{
/* 0x0000-0x7fff: ROM */
case (0x0000 >> 13):
case (0x2000 >> 13):
case (0x4000 >> 13):
case (0x6000 >> 13):
DebugMessage(M64MSG_VERBOSE, "Trying to write to GB ROM %04x", address);
break;
/* 0xa000-0xbfff: RAM */
case (0xa000 >> 13):
write_ram(gb_cart->ram_storage, gb_cart->iram_storage, 1, address - 0xa000, data, size, UINT8_C(0xff));
break;
default:
DebugMessage(M64MSG_WARNING, "Invalid cart write (nombc): %04x", address);
}
return 0;
}
static int read_gb_cart_mbc1(struct gb_cart* gb_cart, uint16_t address, uint8_t* data, size_t size)
{
switch(address >> 13)
{
/* 0x0000-0x3fff: ROM bank 00 */
case (0x0000 >> 13):
case (0x2000 >> 13):
read_rom(gb_cart->rom_storage, gb_cart->irom_storage, address, data, size);
break;
/* 0x4000-0x7fff: ROM bank 01-7f */
case (0x4000 >> 13):
case (0x6000 >> 13):
read_rom(gb_cart->rom_storage, gb_cart->irom_storage, (address - 0x4000) + (gb_cart->rom_bank * 0x4000), data, size);
break;
/* 0xa000-0xbfff: RAM bank 00-03 */
case (0xa000 >> 13):
read_ram(gb_cart->ram_storage, gb_cart->iram_storage, gb_cart->ram_enable, (address - 0xa000) + (gb_cart->ram_bank * 0x2000), data, size, UINT8_C(0xff));
break;
default:
DebugMessage(M64MSG_WARNING, "Invalid cart read (MBC1): %04x", address);
}
return 0;
}
static int write_gb_cart_mbc1(struct gb_cart* gb_cart, uint16_t address, const uint8_t* data, size_t size)
{
uint8_t bank;
uint8_t value = data[size-1];
switch(address >> 13)
{
/* 0x0000-0x1fff: RAM enable */
case (0x0000 >> 13):
set_ram_enable(gb_cart, value);
break;
/* 0x2000-0x3fff: ROM bank select (low 5 bits) */
case (0x2000 >> 13):
bank = value & 0x1f;
gb_cart->rom_bank = (gb_cart->rom_bank & ~UINT8_C(0x1f)) | ((bank == 0) ? 1 : bank);
DebugMessage(M64MSG_VERBOSE, "MBC1 set rom bank %02x", gb_cart->rom_bank);
break;
/* 0x4000-0x5fff: RAM bank / upper ROM bank select (2 bits) */
case (0x4000 >> 13):
bank = value & 0x3;
if (gb_cart->mbc1_mode == 0) {
/* ROM mode */
gb_cart->rom_bank = (gb_cart->rom_bank & 0x1f) | (bank << 5);
}
else {
/* RAM mode */
gb_cart->ram_bank = bank;
}
DebugMessage(M64MSG_VERBOSE, "MBC1 set ram bank %02x", gb_cart->ram_bank);
break;
/* 0x6000-0x7fff: ROM/RAM mode (1 bit) */
case (0x6000 >> 13):
gb_cart->mbc1_mode = (value & 0x1);
if (gb_cart->mbc1_mode == 0) {
/* only RAM bank 0 is accessible in ROM mode */
gb_cart->ram_bank = 0;
} else {
/* only ROM banks 0x01 - 0x1f are accessible in RAM mode */
gb_cart->rom_bank &= 0x1f;
}
break;
/* 0xa000-0xbfff: RAM bank 00-03 */
case (0xa000 >> 13):
write_ram(gb_cart->ram_storage, gb_cart->iram_storage, gb_cart->ram_enable, (address - 0xa000) + (gb_cart->ram_bank * 0x2000), data, size, UINT8_C(0xff));
break;
default:
DebugMessage(M64MSG_WARNING, "Invalid cart write (MBC1): %04x", address);
}
return 0;
}
static int read_gb_cart_mbc2(struct gb_cart* gb_cart, uint16_t address, uint8_t* data, size_t size)
{
switch (address >> 13)
{
/* 0x0000-0x3fff: ROM bank 00 */
case (0x0000 >> 13):
case (0x2000 >> 13):
read_rom(gb_cart->rom_storage, gb_cart->irom_storage, address, data, size);
break;
/* 0x4000-0x7fff: ROM bank 01-0f */
case (0x4000 >> 13):
case (0x6000 >> 13):
read_rom(gb_cart->rom_storage, gb_cart->irom_storage, (address - 0x4000) + (gb_cart->rom_bank * 0x4000), data, size);
break;
/* 0xa000-0xa1ff: internal 512x4bit RAM */
case (0xa000 >> 13):
read_ram(gb_cart->ram_storage, gb_cart->iram_storage, gb_cart->ram_enable, (address - 0xa000), data, size, UINT8_C(0x0f));
break;
default:
DebugMessage(M64MSG_WARNING, "Invalid cart read (MBC2): %04x", address);
}
return 0;
}
static int write_gb_cart_mbc2(struct gb_cart* gb_cart, uint16_t address, const uint8_t* data, size_t size)
{
uint8_t bank;
uint8_t value = data[size-1];
switch(address >> 13)
{
/* 0x0000-0x1fff: RAM enable */
case (0x0000 >> 13):
if ((address & 0x0100) == 0) {
set_ram_enable(gb_cart, value);
}
break;
/* 0x2000-0x3fff: ROM bank select (low 4 bits) */
case (0x2000 >> 13):
if ((address & 0x0100) != 0) {
bank = value & 0x0f;
gb_cart->rom_bank = (bank == 0) ? 1 : bank;
DebugMessage(M64MSG_VERBOSE, "MBC2 set rom bank %02x", gb_cart->rom_bank);
}
break;
/* 0xa000-0xa1ff: internal 512x4bit RAM */
case (0xa000 >> 13):
write_ram(gb_cart->ram_storage, gb_cart->iram_storage, gb_cart->ram_enable, (address - 0xa000), data, size, UINT8_C(0x0f));
break;
default:
DebugMessage(M64MSG_WARNING, "Invalid cart write (MBC2): %04x", address);
}
return 0;
}
static int read_gb_cart_mbc3(struct gb_cart* gb_cart, uint16_t address, uint8_t* data, size_t size)
{
switch(address >> 13)
{
/* 0x0000-0x3fff: ROM bank 00 */
case (0x0000 >> 13):
case (0x2000 >> 13):
read_rom(gb_cart->rom_storage, gb_cart->irom_storage, address, data, size);
break;
/* 0x4000-0x7fff: ROM bank 01-7f */
case (0x4000 >> 13):
case (0x6000 >> 13):
read_rom(gb_cart->rom_storage, gb_cart->irom_storage, (address - 0x4000) + (gb_cart->rom_bank * 0x4000), data, size);
break;
/* 0xa000-0xbfff: RAM bank 00-07 or RTC register 08-0c */
case (0xa000 >> 13):
switch(gb_cart->ram_bank)
{
/* RAM banks */
case 0x00:
case 0x01:
case 0x02:
case 0x03:
case 0x04:
case 0x05:
case 0x06:
case 0x07:
read_ram(gb_cart->ram_storage, gb_cart->iram_storage, gb_cart->ram_enable, (address - 0xa000) + (gb_cart->ram_bank * 0x2000), data, size, UINT8_C(0xff));
break;
/* RTC registers */
case 0x08:
case 0x09:
case 0x0a:
case 0x0b:
case 0x0c:
/* RAM has to be enabled before use */
if (!gb_cart->ram_enable) {
DebugMessage(M64MSG_WARNING, "Trying to read from non enabled GB RAM %04x", address);
memset(data, 0xff, size);
break;
}
if (!(gb_cart->extra_devices & GED_RTC)) {
DebugMessage(M64MSG_WARNING, "Trying to read from absent RTC %04x", address);
memset(data, 0xff, size);
break;
}
memset(data, read_mbc3_rtc_regs(&gb_cart->rtc, gb_cart->ram_bank - 0x08), size);
break;
default:
DebugMessage(M64MSG_WARNING, "Unknown device mapped in RAM/RTC space: %04x", address);
}
break;
default:
DebugMessage(M64MSG_WARNING, "Invalid cart read (MBC3): %04x", address);
}
return 0;
}
static int write_gb_cart_mbc3(struct gb_cart* gb_cart, uint16_t address, const uint8_t* data, size_t size)
{
uint8_t bank;
uint8_t value = data[size-1];
switch(address >> 13)
{
/* 0x0000-0x1fff: RAM/RTC enable */
case (0x0000 >> 13):
set_ram_enable(gb_cart, value);
break;
/* 0x2000-0x3fff: ROM bank select */
case (0x2000 >> 13):
bank = value & 0x7f;
gb_cart->rom_bank = (bank == 0) ? 1 : bank;
DebugMessage(M64MSG_VERBOSE, "MBC3 set rom bank %02x", gb_cart->rom_bank);
break;
/* 0x4000-0x5fff: RAM bank / RTC register select */
case (0x4000 >> 13):
gb_cart->ram_bank = value;
DebugMessage(M64MSG_VERBOSE, "MBC3 set ram bank %02x", gb_cart->ram_bank);
break;
/* 0x6000-0x7fff: latch clock registers */
case (0x6000 >> 13):
if (!(gb_cart->extra_devices & GED_RTC)) {
DebugMessage(M64MSG_WARNING, "Trying to latch to absent RTC %04x", address);
break;
}
latch_mbc3_rtc_regs(&gb_cart->rtc, value);
break;
/* 0xa000-0xbfff: RAM bank 00-07 or RTC register 08-0c */
case (0xa000 >> 13):
switch(gb_cart->ram_bank)
{
/* RAM banks */
case 0x00:
case 0x01:
case 0x02:
case 0x03:
case 0x04:
case 0x05:
case 0x06:
case 0x07:
write_ram(gb_cart->ram_storage, gb_cart->iram_storage, gb_cart->ram_enable, (address - 0xa000) + (gb_cart->ram_bank * 0x2000), data, size, UINT8_C(0xff));
break;
/* RTC registers */
case 0x08:
case 0x09:
case 0x0a:
case 0x0b:
case 0x0c:
/* RAM has to be enabled before use */
if (!gb_cart->ram_enable) {
DebugMessage(M64MSG_WARNING, "Trying to write to non enabled GB RAM %04x", address);
break;
}
if (!(gb_cart->extra_devices & GED_RTC)) {
DebugMessage(M64MSG_WARNING, "Trying to write to absent RTC %04x", address);
break;
}
write_mbc3_rtc_regs(&gb_cart->rtc, gb_cart->ram_bank - 0x08, value);
break;
default:
DebugMessage(M64MSG_WARNING, "Unknwown device mapped in RAM/RTC space: %04x", address);
}
break;
default:
DebugMessage(M64MSG_WARNING, "Invalid cart write (MBC3): %04x", address);
}
return 0;
}
static int read_gb_cart_mbc5(struct gb_cart* gb_cart, uint16_t address, uint8_t* data, size_t size)
{
switch(address >> 13)
{
/* 0x0000-0x3fff: ROM bank 00 */
case (0x0000 >> 13):
case (0x2000 >> 13):
read_rom(gb_cart->rom_storage, gb_cart->irom_storage, address, data, size);
break;
/* 0x4000-0x7fff: ROM bank 00-ff (???) */
case (0x4000 >> 13):
case (0x6000 >> 13):
read_rom(gb_cart->rom_storage, gb_cart->irom_storage, (address - 0x4000) + (gb_cart->rom_bank * 0x4000), data, size);
break;
/* 0xa000-0xbfff: RAM bank 00-07 */
case (0xa000 >> 13):
read_ram(gb_cart->ram_storage, gb_cart->iram_storage, gb_cart->ram_enable, (address - 0xa000) + ((gb_cart->ram_bank & 0x7) * 0x2000), data, size, UINT8_C(0xff));
break;
default:
DebugMessage(M64MSG_WARNING, "Invalid cart read (MBC5): %04x", address);
}
return 0;
}
static int write_gb_cart_mbc5(struct gb_cart* gb_cart, uint16_t address, const uint8_t* data, size_t size)
{
uint8_t value = data[size-1];
switch(address >> 13)
{
/* 0x0000-0x1fff: RAM enable */
case (0x0000 >> 13):
set_ram_enable(gb_cart, value);
break;
/* 0x2000-0x3fff: ROM bank select */
case (0x2000 >> 13):
if (address < 0x3000)
{
gb_cart->rom_bank &= 0xff00;
gb_cart->rom_bank |= value;
}
else
{
gb_cart->rom_bank &= 0x00ff;
gb_cart->rom_bank |= (value & 0x01) << 8;
}
DebugMessage(M64MSG_VERBOSE, "MBC5 set rom bank %04x", gb_cart->rom_bank);
break;
/* 0x4000-0x5fff: RAM bank select */
case (0x4000 >> 13):
gb_cart->ram_bank = value & 0x0f;
if (gb_cart->extra_devices & GED_RUMBLE) {
gb_cart->irumble->exec(gb_cart->rumble, ((gb_cart->ram_bank & 0x8) == 0) ? RUMBLE_STOP : RUMBLE_START);
}
DebugMessage(M64MSG_VERBOSE, "MBC5 set ram bank %02x", gb_cart->ram_bank);
break;
/* 0xa000-0xbfff: RAM bank 00-0f */
case (0xa000 >> 13):
write_ram(gb_cart->ram_storage, gb_cart->iram_storage, gb_cart->ram_enable, (address - 0xa000) + ((gb_cart->ram_bank & 0x07)* 0x2000), data, size, UINT8_C(0xff));
break;
default:
DebugMessage(M64MSG_WARNING, "Invalid cart write (MBC5): %04x", address);
}
return 0;
}
static int read_gb_cart_mbc6(struct gb_cart* gb_cart, uint16_t address, uint8_t* data, size_t size)
{
return 0;
}
static int write_gb_cart_mbc6(struct gb_cart* gb_cart, uint16_t address, const uint8_t* data, size_t size)
{
return 0;
}
static int read_gb_cart_mbc7(struct gb_cart* gb_cart, uint16_t address, uint8_t* data, size_t size)
{
return 0;
}
static int write_gb_cart_mbc7(struct gb_cart* gb_cart, uint16_t address, const uint8_t* data, size_t size)
{
return 0;
}
static int read_gb_cart_mmm01(struct gb_cart* gb_cart, uint16_t address, uint8_t* data, size_t size)
{
return 0;
}
static int write_gb_cart_mmm01(struct gb_cart* gb_cart, uint16_t address, const uint8_t* data, size_t size)
{
return 0;
}
/* TODO: extract each mbc into its own module, store in gb_cart a union of them */
static uint8_t apply_dithering_matrix(const uint8_t* d, uint8_t value, unsigned int x, unsigned int y)
{
d += ((y & 3) * 4 + (x & 3)) * 3;
if (value < d[0]) return 0x00; /* black */
else if (value < d[1]) return 0x40; /* dark gray */
else if (value < d[2]) return 0x80; /* light gray */
return 0xc0; /* white */
}
static void grab_pocket_cam_image(struct pocket_cam* cam)
{
static const uint8_t pm[4][2] = {
{ 0x00, 0x01 }, /* negative */
{ 0x01, 0x00 }, /* positive */
{ 0x01, 0x02 }, /* edge detect */
{ 0x01, 0x02 } /* edge detect */
};
uint8_t regs[M64282FP_REGS_COUNT];
uint8_t tiles[16][16][16];
uint8_t bgr[M64282FP_SENSOR_H][M64282FP_SENSOR_W][3];
uint8_t img[M64282FP_SENSOR_H][M64282FP_SENSOR_W];
unsigned int x, y;
/* setup sensor regs */
unsigned int pm_mode = (cam->regs[0] >> 1) & 0x3;
regs[M64282FP_Z_O] = cam->regs[5];
regs[M64282FP_N_VH_G] = cam->regs[1];
regs[M64282FP_C_LO] = cam->regs[3];
regs[M64282FP_C_HI] = cam->regs[2];
regs[M64282FP_P] = pm[pm_mode][0];
regs[M64282FP_M] = pm[pm_mode][1];
regs[M64282FP_X] = 0x01;
regs[M64282FP_E_I_V] = cam->regs[4];
#if 0
DebugMessage(M64MSG_INFO, "GB cam regs:\n"
"P=%02x M=%02x X=%02x N_VH_G=%02x C=%04x E_I_V=%02x Z_O=%02x\n"
"%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n"
"%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n"
"%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n"
"%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x",
regs[M64282FP_P], regs[M64282FP_M], regs[M64282FP_X], regs[M64282FP_N_VH_G],
regs[M64282FP_C_HI] << 8 | regs[M64282FP_C_LO], regs[M64282FP_E_I_V], regs[M64282FP_Z_O],
cam->regs[6], cam->regs[7], cam->regs[8],
cam->regs[9], cam->regs[10], cam->regs[11],
cam->regs[12], cam->regs[13], cam->regs[14],
cam->regs[15], cam->regs[16], cam->regs[17],
cam->regs[18], cam->regs[19], cam->regs[20],
cam->regs[21], cam->regs[22], cam->regs[23],
cam->regs[24], cam->regs[25], cam->regs[26],
cam->regs[27], cam->regs[28], cam->regs[29],
cam->regs[30], cam->regs[31], cam->regs[32],
cam->regs[33], cam->regs[34], cam->regs[35],
cam->regs[36], cam->regs[37], cam->regs[38],
cam->regs[39], cam->regs[40], cam->regs[41],
cam->regs[42], cam->regs[43], cam->regs[44],
cam->regs[45], cam->regs[46], cam->regs[47],
cam->regs[48], cam->regs[49], cam->regs[50],
cam->regs[51], cam->regs[52], cam->regs[53]);
#endif
/* grab BGR image */
if (cam->ivcap->grab_image(cam->vcap, bgr) != M64ERR_SUCCESS) {
memset(cam->ram, UINT8_C(0xff), sizeof(tiles));
return;
}
cv_imshow("bgr image", M64282FP_SENSOR_W, M64282FP_SENSOR_H, 3, bgr);
/* convert to gray using (2R+5G+1B)/8 formula */
const uint8_t* bgr_ptr = &bgr[0][0][0];
uint8_t* gray_ptr = &img[0][0];
for (y = 0; y < M64282FP_SENSOR_H; ++y) {
for (x = 0; x < M64282FP_SENSOR_W; ++x) {
gray_ptr[0] = (1*bgr_ptr[0] + 5*bgr_ptr[1] + 2*bgr_ptr[2]) / 8;
bgr_ptr += 3;
gray_ptr += 1;
}
}
/* apply m64282fp processings */
cv_imshow("gray image", M64282FP_SENSOR_W, M64282FP_SENSOR_H, 1, img);
process_m64282fp_image(img, regs);
cv_imshow("m64282fp", M64282FP_SENSOR_W, M64282FP_SENSOR_H, 1, img);
/* convert to dithered GB tile format */
memset(tiles, 0, sizeof(tiles));
for (y = 0; y < M64282FP_SENSOR_H; ++y) {
for (x = 0; x < M64282FP_SENSOR_W; ++x) {
/* apply dithering matrix */
uint8_t c = UINT8_C(0xc0) - apply_dithering_matrix(&cam->regs[6], img[y][x], x, y);
img[y][x] = c;
/* encode as tiles */
uint8_t* tile_base = &tiles[y >> 3][x >> 3][(y & 7) << 1];
if (c & 0x40) { tile_base[0] |= (1U << (7 - (7 & x))); }
if (c & 0x80) { tile_base[1] |= (1U << (7 - (7 & x))); }
}
}
cv_imshow("dithered", M64282FP_SENSOR_W, M64282FP_SENSOR_H, 1, img);
/* copy to ram */
memcpy(cam->ram, tiles, sizeof(tiles));
}
static void init_pocket_cam(struct pocket_cam* cam, uint8_t* ram, void* vcap, const struct video_capture_backend_interface* ivcap)
{
cam->ram = ram;
cam->vcap = vcap;
cam->ivcap = ivcap;
}
static void poweron_pocket_cam(struct pocket_cam* cam)
{
memset(cam->regs, 0, POCKET_CAM_REGS_COUNT*sizeof(cam->regs[0]));
}
static uint8_t read_pocket_cam_regs(struct pocket_cam* cam, unsigned int reg)
{
/* All registers except reg 0 are write-only and therefore return 0x00 */
return (reg == 0)
? cam->regs[0]
: 0x00;
}
static void write_pocket_cam_regs(struct pocket_cam* cam, unsigned int reg, uint8_t value)
{
if (reg >= POCKET_CAM_REGS_COUNT) {
#if 0 /* silence it because it happens always (2x32 bytes writes go past 0x36 registers) */
DebugMessage(M64MSG_WARNING, "Out of range camera register write %02x = %02x",
reg, value);
#endif
return;
}
cam->regs[reg] = value;
if (reg == 0) {
cam->regs[reg] &= 0x7;
if (cam->regs[reg] & 0x1) {
grab_pocket_cam_image(cam);
cam->regs[reg] &= ~0x01;
}
}
}
static int read_gb_cart_pocket_cam(struct gb_cart* gb_cart, uint16_t address, uint8_t* data, size_t size)
{
switch(address >> 13)
{
/* 0x0000-0x3fff: ROM bank 00 */
case (0x0000 >> 13):
case (0x2000 >> 13):
read_rom(gb_cart->rom_storage, gb_cart->irom_storage, address, data, size);
break;
/* 0x4000-0x7fff: ROM bank 00-3f */
case (0x4000 >> 13):
case (0x6000 >> 13):
read_rom(gb_cart->rom_storage, gb_cart->irom_storage, (address - 0x4000) + (gb_cart->rom_bank * 0x4000), data, size);
break;
/* 0xa000-0xbfff: RAM bank 00-0f, Camera registers & 0x10 */
case (0xa000 >> 13):
if (gb_cart->ram_bank & 0x10) {
size_t i;
for(i = 0; i < size; ++i) {
data[i] = read_pocket_cam_regs(&gb_cart->cam, ((address+i) & 0x7f));
}
}
else {
read_ram(gb_cart->ram_storage, gb_cart->iram_storage, 1, (address - 0xa000) + (gb_cart->ram_bank * 0x2000), data, size, UINT8_C(0xff));
}
break;
default:
DebugMessage(M64MSG_WARNING, "Invalid cart read (cam): %04x", address);
}
return 0;
}
static int write_gb_cart_pocket_cam(struct gb_cart* gb_cart, uint16_t address, const uint8_t* data, size_t size)
{
uint8_t value = data[size-1];
switch(address >> 13)
{
/* 0x0000-0x1fff: RAM write enable */
case (0x0000 >> 13):
set_ram_enable(gb_cart, value);
break;
/* 0x2000-0x3fff: ROM bank select */
case (0x2000 >> 13):
gb_cart->rom_bank = (value & 0x3f);
DebugMessage(M64MSG_VERBOSE, "CAM set rom bank %04x", gb_cart->rom_bank);
break;
/* 0x4000-0x5fff: RAM bank select */
case (0x4000 >> 13):
if (value & 0x10) {
gb_cart->ram_bank = value;
DebugMessage(M64MSG_VERBOSE, "CAM set register bank %02x", gb_cart->ram_bank);
}
else {
gb_cart->ram_bank = (value & 0x0f);
DebugMessage(M64MSG_VERBOSE, "CAM set ram bank %02x", gb_cart->ram_bank);
}
break;
/* 0xa000-0xbfff: RAM bank 00-0f, Camera registers & 0x10 */
case (0xa000 >> 13):
if (gb_cart->ram_bank & 0x10) {
size_t i;
for(i = 0; i < size; ++i) {
write_pocket_cam_regs(&gb_cart->cam, ((address+i) & 0x7f), data[i]);
}
}
else {
write_ram(gb_cart->ram_storage, gb_cart->iram_storage, gb_cart->ram_enable, (address - 0xa000) + (gb_cart->ram_bank * 0x2000), data, size, UINT8_C(0xff));
}
break;
default:
DebugMessage(M64MSG_WARNING, "Invalid cart write (cam): %04x", address);
}
return 0;
}
static int read_gb_cart_bandai_tama5(struct gb_cart* gb_cart, uint16_t address, uint8_t* data, size_t size)
{
return 0;
}
static int write_gb_cart_bandai_tama5(struct gb_cart* gb_cart, uint16_t address, const uint8_t* data, size_t size)
{
return 0;
}
static int read_gb_cart_huc1(struct gb_cart* gb_cart, uint16_t address, uint8_t* data, size_t size)
{
return 0;
}
static int write_gb_cart_huc1(struct gb_cart* gb_cart, uint16_t address, const uint8_t* data, size_t size)
{
return 0;
}
static int read_gb_cart_huc3(struct gb_cart* gb_cart, uint16_t address, uint8_t* data, size_t size)
{
return 0;
}
static int write_gb_cart_huc3(struct gb_cart* gb_cart, uint16_t address, const uint8_t* data, size_t size)
{
return 0;
}
struct parsed_cart_type
{
const char* mbc;
int (*read_gb_cart)(struct gb_cart*,uint16_t,uint8_t*,size_t);
int (*write_gb_cart)(struct gb_cart*,uint16_t,const uint8_t*,size_t);
unsigned int extra_devices;
};
static const struct parsed_cart_type* parse_cart_type(uint8_t cart_type)
{
#define MBC(x) #x, read_gb_cart_ ## x, write_gb_cart_ ## x
static const struct parsed_cart_type nombc_none = { MBC(nombc), GED_NONE };
static const struct parsed_cart_type nombc_ram = { MBC(nombc), GED_RAM };
static const struct parsed_cart_type nombc_ram_batt = { MBC(nombc), GED_RAM | GED_BATTERY };
static const struct parsed_cart_type mbc1_none = { MBC(mbc1), GED_NONE };
static const struct parsed_cart_type mbc1_ram = { MBC(mbc1), GED_RAM };
static const struct parsed_cart_type mbc1_ram_batt = { MBC(mbc1), GED_RAM | GED_BATTERY };
static const struct parsed_cart_type mbc2_none = { MBC(mbc2), GED_NONE };
static const struct parsed_cart_type mbc2_ram_batt = { MBC(mbc2), GED_RAM | GED_BATTERY };
static const struct parsed_cart_type mmm01_none = { MBC(mmm01), GED_NONE };
static const struct parsed_cart_type mmm01_ram = { MBC(mmm01), GED_RAM };
static const struct parsed_cart_type mmm01_ram_batt = { MBC(mmm01), GED_RAM | GED_BATTERY };
static const struct parsed_cart_type mbc3_none = { MBC(mbc3), GED_NONE };
static const struct parsed_cart_type mbc3_ram = { MBC(mbc3), GED_RAM };
static const struct parsed_cart_type mbc3_ram_batt = { MBC(mbc3), GED_RAM | GED_BATTERY };
static const struct parsed_cart_type mbc3_batt_rtc = { MBC(mbc3), GED_BATTERY | GED_RTC };
static const struct parsed_cart_type mbc3_ram_batt_rtc = { MBC(mbc3), GED_RAM | GED_BATTERY | GED_RTC };
static const struct parsed_cart_type mbc5_none = { MBC(mbc5), GED_NONE };
static const struct parsed_cart_type mbc5_ram = { MBC(mbc5), GED_RAM };
static const struct parsed_cart_type mbc5_ram_batt = { MBC(mbc5), GED_RAM | GED_BATTERY };
static const struct parsed_cart_type mbc5_rumble = { MBC(mbc5), GED_RUMBLE };
static const struct parsed_cart_type mbc5_ram_rumble = { MBC(mbc5), GED_RAM | GED_RUMBLE };
static const struct parsed_cart_type mbc5_ram_batt_rumble = { MBC(mbc5), GED_RAM | GED_BATTERY | GED_RUMBLE };
static const struct parsed_cart_type mbc6 = { MBC(mbc6), GED_RAM | GED_BATTERY };
static const struct parsed_cart_type mbc7 = { MBC(mbc7), GED_RAM | GED_BATTERY | GED_ACCELEROMETER };
static const struct parsed_cart_type pocket_cam = { MBC(pocket_cam), GED_RAM | GED_CAMERA };
static const struct parsed_cart_type bandai_tama5 = { MBC(bandai_tama5), GED_NONE };
static const struct parsed_cart_type huc3 = { MBC(huc3), GED_NONE };
static const struct parsed_cart_type huc1 = { MBC(huc1), GED_RAM | GED_BATTERY };
#undef MBC
switch(cart_type)
{
case 0x00: return &nombc_none;
case 0x01: return &mbc1_none;
case 0x02: return &mbc1_ram;
case 0x03: return &mbc1_ram_batt;
/* 0x04 is unused */
case 0x05: return &mbc2_none;
case 0x06: return &mbc2_ram_batt;
/* 0x07 is unused */
case 0x08: return &nombc_ram;
case 0x09: return &nombc_ram_batt;
/* 0x0a is unused */
case 0x0b: return &mmm01_none;
case 0x0c: return &mmm01_ram;
case 0x0d: return &mmm01_ram_batt;
/* 0x0e is unused */
case 0x0f: return &mbc3_batt_rtc;
case 0x10: return &mbc3_ram_batt_rtc;
case 0x11: return &mbc3_none;
case 0x12: return &mbc3_ram;
case 0x13: return &mbc3_ram_batt;
/* 0x14-0x18 are unused */
case 0x19: return &mbc5_none;
case 0x1a: return &mbc5_ram;
case 0x1b: return &mbc5_ram_batt;
case 0x1c: return &mbc5_rumble;
case 0x1d: return &mbc5_ram_rumble;
case 0x1e: return &mbc5_ram_batt_rumble;
/* 0x1f is unused */
case 0x20: return &mbc6;
/* 0x21 is unused */
case 0x22: return &mbc7;
/* 0x23-0xfb are unused */
case 0xfc: return &pocket_cam;
case 0xfd: return &bandai_tama5;
case 0xfe: return &huc3;
case 0xff: return &huc1;
default: return NULL;
}
}
void init_gb_cart(struct gb_cart* gb_cart,
void* rom_opaque, void (*init_rom)(void* user_data, void** rom_storage, const struct storage_backend_interface** irom_storage), void (*release_rom)(void* user_data),
void* ram_opaque, void (*init_ram)(void* user_data, size_t ram_size, void** ram_storage, const struct storage_backend_interface** iram_storage), void (*release_ram)(void* user_data),
void* clock, const struct clock_backend_interface* iclock,
void* rumble, const struct rumble_backend_interface* irumble,
void* vcap, const struct video_capture_backend_interface* ivcap)
{
const struct parsed_cart_type* type;
void* rom_storage = NULL;
const struct storage_backend_interface* irom_storage = NULL;
void* ram_storage = NULL;
const struct storage_backend_interface* iram_storage = NULL;
struct mbc3_rtc rtc;
struct pocket_cam cam;
memset(&rtc, 0, sizeof(rtc));
memset(&cam, 0, sizeof(cam));
/* ask to load rom and initialize rom storage backend */
init_rom(rom_opaque, &rom_storage, &irom_storage);
/* handle no cart case */
if (irom_storage == NULL) {
goto no_cart;
}
/* check rom size */
const uint8_t* rom_data = irom_storage->data(rom_storage);
if (rom_data == NULL || irom_storage->size(rom_storage) < 0x8000)
{
DebugMessage(M64MSG_ERROR, "Invalid GB ROM file size (< 32k)");
goto error_release_rom;
}
/* get and parse cart type */
uint8_t cart_type = rom_data[0x147];
type = parse_cart_type(cart_type);
if (type == NULL)
{
DebugMessage(M64MSG_ERROR, "Invalid GB cart type (%02x)", cart_type);
goto error_release_rom;
}
DebugMessage(M64MSG_INFO, "GB cart type (%02x) %s%s%s%s%s%s%s",
cart_type,
type->mbc,
(type->extra_devices & GED_RAM) ? " RAM" : "",
(type->extra_devices & GED_BATTERY) ? " BATT" : "",
(type->extra_devices & GED_RTC) ? " RTC" : "",
(type->extra_devices & GED_RUMBLE) ? " RUMBLE" : "",
(type->extra_devices & GED_ACCELEROMETER) ? " ACCEL" : "",
(type->extra_devices & GED_CAMERA) ? " CAM" : "");
/* load ram (if present) */
if (type->extra_devices & GED_RAM)
{
size_t ram_size = 0;
switch(rom_data[0x149])
{
case 0x00: ram_size = (strcmp(type->mbc, "mbc2") == 0)
? 0x200 /* MBC2 have an integrated 512x4bit RAM */
: 0;
break;
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)
{
init_ram(ram_opaque, ram_size, &ram_storage, &iram_storage);
/* check that init_ram succeeded */
if (iram_storage == NULL) {
DebugMessage(M64MSG_ERROR, "Failed to initialize GB RAM");
goto error_release_ram;
}
if (iram_storage->data(ram_storage) == NULL || iram_storage->size(ram_storage) != ram_size)
{
DebugMessage(M64MSG_ERROR, "Cannot get GB RAM (%d bytes)", (uint32_t) ram_size);
goto error_release_ram;
}
DebugMessage(M64MSG_INFO, "Using a %d bytes GB RAM", (uint32_t) ram_size);
}
}
/* set RTC clock (if present) */
if (type->extra_devices & GED_RTC) {
init_mbc3_rtc(&rtc, clock, iclock);
}
if (type->extra_devices & GED_CAMERA) {
init_pocket_cam(&cam, iram_storage->data(ram_storage), vcap, ivcap);
}
/* update gb_cart */
gb_cart->rom_storage = rom_storage;
gb_cart->irom_storage = irom_storage;
gb_cart->ram_storage = ram_storage;
gb_cart->iram_storage = iram_storage;
gb_cart->extra_devices = type->extra_devices;
gb_cart->rtc = rtc;
gb_cart->cam = cam;
gb_cart->rumble = rumble;
gb_cart->irumble = irumble;
gb_cart->read_gb_cart = type->read_gb_cart;
gb_cart->write_gb_cart = type->write_gb_cart;
return;
error_release_ram:
release_ram(ram_opaque);
error_release_rom:
release_rom(rom_opaque);
no_cart:
memset(gb_cart, 0, sizeof(*gb_cart));
}
void poweron_gb_cart(struct gb_cart* gb_cart)
{
gb_cart->rom_bank = 1;
gb_cart->ram_bank = 0;
gb_cart->ram_enable = 0;
gb_cart->mbc1_mode = 0;
if (gb_cart->extra_devices & GED_RTC) {
poweron_mbc3_rtc(&gb_cart->rtc);
}
if (gb_cart->extra_devices & GED_CAMERA) {
poweron_pocket_cam(&gb_cart->cam);
}
if (gb_cart->extra_devices & GED_RUMBLE) {
gb_cart->irumble->exec(gb_cart->rumble, RUMBLE_STOP);
}
}
int read_gb_cart(struct gb_cart* gb_cart, uint16_t address, uint8_t* data, size_t size)
{
return gb_cart->read_gb_cart(gb_cart, address, data, size);
}
int write_gb_cart(struct gb_cart* gb_cart, uint16_t address, const uint8_t* data, size_t size)
{
return gb_cart->write_gb_cart(gb_cart, address, data, size);
}