gbemu/lib/cart.c
2025-02-16 10:33:06 -07:00

358 lines
7.8 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <cart.h>
#include <common.h>
#include <string.h>
typedef struct {
char filename[1024];
u32 rom_size;
u8 *rom_data;
rom_header *header;
//mbc1 data
bool ram_enabled;
bool ram_banking;
u8 *rom_bank_x;
u8 banking_mode;
u8 rom_bank_value;
u8 ram_bank_value;
u8 *ram_bank;
u8 *ram_banks[16];
//battery
bool battery;
bool need_save;
} cart_context;
static cart_context ctx;
bool cart_need_save () {
return ctx.need_save;
}
bool cart_mbc1() {
return BETWEEN(ctx.header->type, 1, 3);
}
bool cart_mbc3() {
return BETWEEN(ctx.header->type, 15, 19);
}
bool cart_mbc() {
return cart_mbc1() || cart_mbc3();
}
bool cart_battery() {
return ctx.header->type == 3 || BETWEEN(ctx.header->type, 15, 16) || ctx.header->type == 19;
}
static const char *ROM_TYPES[] = {
"ROM ONLY",
"MBC1",
"MBC1+RAM",
"MBC1+RAM+BATTERY",
"0X04 ???",
"MBC2",
"MBC2+BATTERY",
"0X07 ???",
"ROM+RAM 1",
"ROM+RAM+BATTERY 1",
"0X0A ???",
"MMM01",
"MMM01+RAM",
"MMM01+RAM+BATTERY",
"0X0E ???",
"MBC3+TIMER+BATTERY",
"MBC3+TIMER+RAM+BATTERY 2",
"MBC3",
"MBC3+RAM 2",
"MBC3+RAM+BATTERY 2",
"0X14 ???",
"0X15 ???",
"0X16 ???",
"0X17 ???",
"0X18 ???",
"MBC5",
"MBC5+RAM",
"MBC5+RAM+BATTERY",
"MBC5+RUMBLE",
"MBC5+RUMBLE+RAM",
"MBC5+RUMBLE+RAM+BATTERY",
"0X1F ???",
"MBC6",
"0X21 ???",
"MBC7+SENSOR+RUMBLE+RAM+BATTERY "
};
static const char *LIC_CODE[0xA5] = {
[0x00] = "None",
[0x01] = "Nintendo R&D1",
[0x08] = "Capcom",
[0x13] = "Electronic Arts",
[0x18] = "Hudson Soft",
[0x19] = "b-ai",
[0x20] = "kss",
[0x22] = "pow",
[0x24] = "PCM Complete",
[0x25] = "san-x",
[0x28] = "Kemco Japan",
[0x29] = "seta",
[0x30] = "Viacom",
[0x31] = "Nintendo",
[0x32] = "Bandai",
[0x33] = "Ocean/Acclaim",
[0x34] = "Konami",
[0x35] = "Hector",
[0x37] = "Taito",
[0x38] = "Hudson",
[0x39] = "Banpresto",
[0x41] = "Ubi Soft",
[0x42] = "Atlus",
[0x44] = "Malibu",
[0x46] = "angel",
[0x47] = "Bullet-Proof",
[0x49] = "irem",
[0x50] = "Absolute",
[0x51] = "Acclaim",
[0x52] = "Activision",
[0x53] = "American sammy",
[0x54] = "Konami",
[0x55] = "Hi tech entertainment",
[0x56] = "LJN",
[0x57] = "Matchbox",
[0x58] = "Mattel",
[0x59] = "Milton Bradley",
[0x60] = "Titus",
[0x61] = "Virgin",
[0x64] = "LucasArts",
[0x67] = "Ocean",
[0x69] = "Electronic Arts",
[0x70] = "Infogrames",
[0x71] = "Interplay",
[0x72] = "Broderbund",
[0x73] = "sculptured",
[0x75] = "sci",
[0x78] = "THQ",
[0x79] = "Accolade",
[0x80] = "misawa",
[0x83] = "lozc",
[0x86] = "Tokuma Shoten Intermedia",
[0x87] = "Tsukuda Original",
[0x91] = "Chunsoft",
[0x92] = "Video system",
[0x93] = "Ocean/Acclaim",
[0x95] = "Varie",
[0x96] = "Yonezawa/spal",
[0x97] = "Kaneko",
[0x99] = "Pack in soft",
[0xA4] = "Konami (Yu-Gi-Oh!)"
};
rom_header *get_rom_header() {
return ctx.header;
}
const char *cart_type_name() {
if(ctx.header->type <= 0x22) {
return ROM_TYPES[ctx.header->type];
}
return "UNKNOWN";
}
const char *cart_lic_name() {
if(ctx.header->new_lic_code <= 0xA4) {
return LIC_CODE[ctx.header->lic_code];
}
return "UNKNOWN";
}
void cart_setup_bamking() {
for (int i=0; i<16; i++) {
ctx.ram_banks[1] = 0;
if((ctx.header->ram_size == 2 && i == 0) ||
(ctx.header->ram_size == 3 && i < 4) ||
(ctx.header->ram_size == 5 && i < 8) ||
(ctx.header->ram_size == 4)) {
ctx.ram_banks[i] = malloc(0x2000);
memset(ctx.ram_banks[i], 0, 0x2000);
}
}
ctx.ram_bank = ctx.ram_banks[0];
ctx.rom_bank_x = ctx.rom_data + 0x4000;
}
bool cart_load(char *cart) {
snprintf(ctx.filename, sizeof(ctx.filename), "%s", cart);
FILE *fp = fopen(cart, "r");
if (!fp) {
printf("Failed to open: %s\n", cart);
return false;
}
printf("Opened: %s\n", cart),
fseek(fp, 0, SEEK_END);
ctx.rom_size = ftell(fp);
rewind(fp);
ctx.rom_data = malloc(ctx.rom_size);
fread(ctx.rom_data, ctx.rom_size, 1, fp);
fclose(fp);
ctx.header = (rom_header *)(ctx.rom_data + 0x100);
ctx.header->title[15] = 0;
ctx.battery = cart_battery();
ctx.need_save = false;
printf("Cartridge Loaded:\n");
printf("\t Title : %s\n", ctx.header->title);
printf("\t Type : %2.2X (%s)\n", ctx.header->type, cart_type_name());
printf("\t ROM Size : %d KB\n", 32 << ctx.header->rom_size);
printf("\t RAM Size : %2.2X\n", ctx.header->ram_size);
printf("\t LIC Code : %2.2X (%s)\n", ctx.header->lic_code, cart_lic_name());
printf("\t ROM Vers : %2.2X\n", ctx.header->version);
cart_setup_bamking();
u16 x = 0;
for (u16 i=0X0134; i<=0X014C; i++) {
x = x - ctx.rom_data[i] -1;
}
printf("\t Checksum : %2.2X (%s)\n", ctx.header->checksum, (x & 0xFF) == ctx.header->checksum ? "PASSED" : "Failed");
if((x & 0xFF) != ctx.header->checksum) {
fprintf(stderr, "WARNING!!! The header checksum does not match! ROM may be corrupt or invalid!\n");
}
if(ctx.battery) {
cart_battery_load();
}
return true;
}
u8 cart_read(u16 address){
if(!cart_mbc() || address < 0x4000) {
return ctx.rom_data[address];
}
if((address & 0xE000) == 0xA000) {
if (!ctx.ram_enabled) {
return 0xFF;
}
if(!ctx.ram_bank) {
return 0xFF;
}
return ctx.ram_bank[address - 0xA000];
}
return ctx.rom_bank_x[address - 0x4000];
}
void cart_write(u16 address, u8 value){
//printf("Cart Write: %04X\n", address);
if(!cart_mbc()) {
return;
}
if(address < 0x2000) {
ctx.ram_enabled = ((value & 0xF) == 0xA);
}
if((address & 0xE000) == 0x2000) {
//rom bank
if(value == 0) {
value = 1;
}
if(cart_mbc3()) {
value &= 0b1111111;
ctx.rom_bank_value = value;
ctx.rom_bank_x = ctx.rom_data + (0x4000 * ctx.rom_bank_value);
}else {
value &= 0b11111;
ctx.rom_bank_value = value;
ctx.rom_bank_x = ctx.rom_data + (0x4000 * ctx.rom_bank_value);
}
}
if((address & 0xE000) == 0x4000) {
//ram bank number
ctx.ram_bank_value = value & 0xb11;
if(ctx.ram_banking) {
if(cart_need_save) {
cart_battery_save();
}
ctx.ram_bank = ctx.ram_banks[ctx.ram_bank_value];
}
}
if((address & 0xE000) == 0x6000) {
//banking mode select
if(cart_mbc1()){
ctx.banking_mode = value & 1;
ctx.ram_banking = ctx.banking_mode;
if (ctx.ram_banking) {
if(cart_need_save) {
cart_battery_save();
}
ctx.ram_bank = ctx.ram_banks[ctx.ram_bank_value];
}
}
}
if((address & 0xE000) == 0xA000) {
if(!ctx.ram_enabled)
return;
if(!ctx.ram_bank)
return;
ctx.ram_bank[address - 0xA000] = value;
if(ctx.battery) {
ctx.need_save = true;
}
}
}
bool cart_battery_load(){
char fn[1048];
sprintf(fn, "%s.battery", ctx.filename);
FILE *fp = fopen(fn, "rb");
if(!fp) {
printf("No battery save found.");
return true;
}
fread(ctx.ram_bank, 0x2000, 1, fp);
fclose(fp);
}
bool cart_battery_save(){
char fn[1048];
sprintf(fn, "%s.battery", ctx.filename);
FILE *fp = fopen(fn, "wb");
if(!fp) {
fprintf(stderr, "unable to open: %s\n", fn);
}
fwrite(ctx.ram_bank, 0x2000, 1, fp);
fclose(fp);
}