git.y1.nz

SameBoy

Accurate GB/GBC emulator
download: https://git.y1.nz/archives/sameboy.tar.gz
README | Files | Log | Refs | LICENSE

Core/mbc.c

      1 #include <stdbool.h>
      2 #include <stdlib.h>
      3 #include <string.h>
      4 #include "gb.h"
      5 
      6 const GB_cartridge_t GB_cart_defs[256] = {
      7     // From http://gbdev.gg8.se/wiki/articles/The_Cartridge_Header#0147_-_Cartridge_Type
      8     /* MBC        RAM    BAT.   RTC    RUMB.   */
      9     {  GB_NO_MBC, false, false, false, false}, // 00h  ROM ONLY
     10     {  GB_MBC1  , false, false, false, false}, // 01h  MBC1
     11     {  GB_MBC1  , true , false, false, false}, // 02h  MBC1+RAM
     12     {  GB_MBC1  , true , true , false, false}, // 03h  MBC1+RAM+BATTERY
     13     [5] =
     14     {  GB_MBC2  , true , false, false, false}, // 05h  MBC2
     15     {  GB_MBC2  , true , true , false, false}, // 06h  MBC2+BATTERY
     16     [8] =
     17     {  GB_NO_MBC, true , false, false, false}, // 08h  ROM+RAM
     18     {  GB_NO_MBC, true , true , false, false}, // 09h  ROM+RAM+BATTERY
     19     [0xB] =
     20     {  GB_MMM01 , false, false, false, false}, // 0Bh  MMM01
     21     {  GB_MMM01 , true , false, false, false}, // 0Ch  MMM01+RAM
     22     {  GB_MMM01 , true , true , false, false}, // 0Dh  MMM01+RAM+BATTERY
     23     [0xF] =
     24     {  GB_MBC3  , false, true,  true , false}, // 0Fh  MBC3+TIMER+BATTERY
     25     {  GB_MBC3  , true , true,  true , false}, // 10h  MBC3+TIMER+RAM+BATTERY
     26     {  GB_MBC3  , false, false, false, false}, // 11h  MBC3
     27     {  GB_MBC3  , true , false, false, false}, // 12h  MBC3+RAM
     28     {  GB_MBC3  , true , true , false, false}, // 13h  MBC3+RAM+BATTERY
     29     [0x19] =
     30     {  GB_MBC5  , false, false, false, false}, // 19h  MBC5
     31     {  GB_MBC5  , true , false, false, false}, // 1Ah  MBC5+RAM
     32     {  GB_MBC5  , true , true , false, false}, // 1Bh  MBC5+RAM+BATTERY
     33     {  GB_MBC5  , false, false, false, true }, // 1Ch  MBC5+RUMBLE
     34     {  GB_MBC5  , true , false, false, true }, // 1Dh  MBC5+RUMBLE+RAM
     35     {  GB_MBC5  , true , true , false, true }, // 1Eh  MBC5+RUMBLE+RAM+BATTERY
     36     [0x22] =
     37     {  GB_MBC7  , true,  true,  false, false}, // 22h  MBC7+ACCEL+EEPROM
     38     [0xFC] =
     39     {  GB_CAMERA, true , true , false, false}, // FCh  POCKET CAMERA
     40     {  GB_NO_MBC, false, false, false, false}, // FDh  BANDAI TAMA5 (Todo: Not supported)
     41     {  GB_HUC3  , true , true , true,  false}, // FEh  HuC3
     42     {  GB_HUC1  , true , true , false, false}, // FFh  HuC1+RAM+BATTERY
     43 };
     44 
     45 void GB_update_mbc_mappings(GB_gameboy_t *gb)
     46 {
     47     switch (gb->cartridge_type->mbc_type) {
     48         case GB_NO_MBC: return;
     49         case GB_MBC1:
     50             switch (gb->mbc1_wiring) {
     51                 case GB_STANDARD_MBC1_WIRING:
     52                     gb->mbc_rom_bank = gb->mbc1.bank_low | (gb->mbc1.bank_high << 5);
     53                     if (gb->mbc1.mode == 0) {
     54                         gb->mbc_ram_bank = 0;
     55                         gb->mbc_rom0_bank = 0;
     56                     }
     57                     else {
     58                         gb->mbc_ram_bank = gb->mbc1.bank_high;
     59                         gb->mbc_rom0_bank = gb->mbc1.bank_high << 5;
     60                     }
     61                     if ((gb->mbc_rom_bank & 0x1F) == 0) {
     62                         gb->mbc_rom_bank++;
     63                     }
     64                     break;
     65                 case GB_MBC1M_WIRING:
     66                     gb->mbc_rom_bank = (gb->mbc1.bank_low & 0xF) | (gb->mbc1.bank_high << 4);
     67                     if (gb->mbc1.mode == 0) {
     68                         gb->mbc_ram_bank = 0;
     69                         gb->mbc_rom0_bank = 0;
     70                     }
     71                     else {
     72                         gb->mbc_rom0_bank = gb->mbc1.bank_high << 4;
     73                         gb->mbc_ram_bank = 0;
     74                     }
     75                     if ((gb->mbc1.bank_low & 0x1F) == 0) {
     76                         gb->mbc_rom_bank++;
     77                     }
     78                     break;
     79                 nodefault;
     80             }
     81             break;
     82         case GB_MBC2:
     83             gb->mbc_rom_bank = gb->mbc2.rom_bank;
     84             if ((gb->mbc_rom_bank & 0xF) == 0) {
     85                 gb->mbc_rom_bank = 1;
     86             }
     87             break;
     88         case GB_MBC3:
     89             gb->mbc_rom_bank = gb->mbc3.rom_bank;
     90             gb->mbc_ram_bank = gb->mbc3.ram_bank;
     91             if (!gb->is_mbc30) {
     92                 gb->mbc_rom_bank &= 0x7F;
     93             }
     94             if (gb->mbc_rom_bank == 0) {
     95                 gb->mbc_rom_bank = 1;
     96             }
     97             break;
     98         case GB_MBC5:
     99         case GB_CAMERA:
    100             gb->mbc_rom_bank = gb->mbc5.rom_bank_low | (gb->mbc5.rom_bank_high << 8);
    101             gb->mbc_ram_bank = gb->mbc5.ram_bank;
    102             break;
    103         case GB_MBC7:
    104             gb->mbc_rom_bank = gb->mbc7.rom_bank;
    105             break;
    106         case GB_MMM01:
    107             if (gb->mmm01.locked) {
    108                 if (gb->mmm01.multiplex_mode) {
    109                     gb->mbc_rom0_bank = (gb->mmm01.rom_bank_low & (gb->mmm01.rom_bank_mask << 1)) |
    110                     ((gb->mmm01.mbc1_mode? 0 : gb->mmm01.ram_bank_low) << 5) |
    111                         (gb->mmm01.rom_bank_high << 7);
    112                     gb->mbc_rom_bank = gb->mmm01.rom_bank_low |
    113                         (gb->mmm01.ram_bank_low << 5) |
    114                         (gb->mmm01.rom_bank_high << 7);
    115                     gb->mbc_ram_bank = gb->mmm01.rom_bank_mid | (gb->mmm01.ram_bank_high << 2);
    116                 }
    117                 else {
    118                     gb->mbc_rom0_bank = (gb->mmm01.rom_bank_low & (gb->mmm01.rom_bank_mask << 1)) |
    119                         (gb->mmm01.rom_bank_mid << 5) |
    120                         (gb->mmm01.rom_bank_high << 7);
    121                     gb->mbc_rom_bank = gb->mmm01.rom_bank_low |
    122                         (gb->mmm01.rom_bank_mid << 5) |
    123                         (gb->mmm01.rom_bank_high << 7);
    124                     if (gb->mmm01.mbc1_mode) {
    125                         gb->mbc_ram_bank = gb->mmm01.ram_bank_low | (gb->mmm01.ram_bank_high << 2);
    126                     }
    127                     else {
    128                         gb->mbc_ram_bank = gb->mmm01.ram_bank_low | (gb->mmm01.ram_bank_high << 2);
    129                     }
    130                 }
    131                 if (gb->mbc_rom_bank == gb->mbc_rom0_bank) {
    132                     gb->mbc_rom_bank++;
    133                 }
    134             }
    135             else {
    136                 gb->mbc_rom_bank = -1;
    137                 gb->mbc_rom0_bank = -2;
    138             }
    139             break;
    140         case GB_HUC1:
    141             gb->mbc_rom_bank = gb->huc1.bank_low;
    142             gb->mbc_ram_bank = gb->huc1.bank_high;
    143             break;
    144         case GB_HUC3:
    145             gb->mbc_rom_bank = gb->huc3.rom_bank;
    146             gb->mbc_ram_bank = gb->huc3.ram_bank;
    147             break;
    148         case GB_TPP1:
    149             gb->mbc_rom_bank = gb->tpp1.rom_bank;
    150             gb->mbc_ram_bank = gb->tpp1.ram_bank;
    151             gb->mbc_ram_enable = (gb->tpp1.mode == 2) || (gb->tpp1.mode == 3);
    152             break;
    153         nodefault;
    154     }
    155 }
    156 
    157 void GB_configure_cart(GB_gameboy_t *gb)
    158 {
    159     memset(GB_GET_SECTION(gb, mbc), 0, GB_SECTION_SIZE(mbc));
    160     gb->cartridge_type = &GB_cart_defs[gb->rom[0x147]];
    161     if (gb->cartridge_type->mbc_type == GB_MMM01) {
    162         uint8_t *temp = malloc(0x8000);
    163         memcpy(temp, gb->rom, 0x8000);
    164         memmove(gb->rom, gb->rom + 0x8000, gb->rom_size - 0x8000);
    165         memcpy(gb->rom + gb->rom_size - 0x8000, temp, 0x8000);
    166         free(temp);
    167     }
    168     else {
    169         const GB_cartridge_t *maybe_mmm01_type = &GB_cart_defs[gb->rom[gb->rom_size - 0x8000 + 0x147]];
    170         if (memcmp(gb->rom + 0x104, gb->rom + gb->rom_size - 0x8000 + 0x104, 0x30) == 0) {
    171             if (maybe_mmm01_type->mbc_type == GB_MMM01) {
    172                 gb->cartridge_type = maybe_mmm01_type;
    173             }
    174             else if(gb->rom[gb->rom_size - 0x8000 + 0x147] == 0x11) {
    175                 GB_log(gb, "ROM header reports MBC3, but it appears to be an MMM01 ROM. Assuming cartridge uses MMM01.");
    176                 gb->cartridge_type = &GB_cart_defs[0xB];
    177             }
    178         }
    179     }
    180 
    181     if (gb->rom[0x147] == 0xBC &&
    182         gb->rom[0x149] == 0xC1 &&
    183         gb->rom[0x14A] == 0x65) {
    184         static const GB_cartridge_t tpp1 = {GB_TPP1, true, true, true, true};
    185         gb->cartridge_type = &tpp1;
    186         gb->tpp1.rom_bank = 1;
    187     }
    188     
    189     if (gb->cartridge_type->mbc_type != GB_MMM01) {
    190         if (gb->rom[0x147] == 0 && gb->rom_size > 0x8000) {
    191             GB_log(gb, "ROM header reports no MBC, but file size is over 32Kb. Assuming cartridge uses MBC3.\n");
    192             gb->cartridge_type = &GB_cart_defs[0x11];
    193         }
    194         else if (gb->rom[0x147] != 0 && memcmp(gb->cartridge_type, &GB_cart_defs[0], sizeof(GB_cart_defs[0])) == 0) {
    195             GB_log(gb, "Cartridge type %02x is not yet supported.\n", gb->rom[0x147]);
    196         }
    197     }
    198     
    199     if (!gb->cartridge_type->has_ram &&
    200         gb->cartridge_type->mbc_type != GB_NO_MBC &&
    201         gb->cartridge_type->mbc_type != GB_TPP1 &&
    202         gb->rom[0x149]) {
    203         GB_log(gb, "ROM header reports no RAM, but also reports a non-zero RAM size. Assuming cartridge has RAM.\n");
    204         gb->cartridge_type++;
    205     }
    206         
    207     size_t old_mbc_ram_size = gb->mbc_ram_size;
    208     gb->mbc_ram_size = 0;
    209     
    210     if (gb->cartridge_type->has_ram) {
    211         if (gb->cartridge_type->mbc_type == GB_MBC2) {
    212             gb->mbc_ram_size = 0x200;
    213         }
    214         else if (gb->cartridge_type->mbc_type == GB_MBC7) {
    215             gb->mbc_ram_size = 0x100;
    216         }
    217         else if (gb->cartridge_type->mbc_type == GB_TPP1) {
    218             if (gb->rom[0x152] >= 1 && gb->rom[0x152] <= 9) {
    219                 gb->mbc_ram_size = 0x2000 << (gb->rom[0x152] - 1);
    220             }
    221         }
    222         else {
    223             static const unsigned ram_sizes[256] = {0, 0x800, 0x2000, 0x8000, 0x20000, 0x10000};
    224             if (gb->cartridge_type->mbc_type == GB_MMM01) {
    225                 gb->mbc_ram_size = ram_sizes[gb->rom[gb->rom_size - 0x8000 + 0x149]];
    226             }
    227             else {
    228                 gb->mbc_ram_size = ram_sizes[gb->rom[0x149]];
    229             }
    230         }
    231         
    232         if (gb->mbc_ram_size && gb->mbc_ram_size < 0x2000 &&
    233             gb->cartridge_type->mbc_type != GB_MBC2 &&
    234             gb->cartridge_type->mbc_type != GB_MBC7) {
    235             GB_log(gb, "This ROM requests a RAM size smaller than a bank, it may misbehave if this was not done intentionally.\n");
    236         }
    237         
    238         if (gb->mbc_ram && old_mbc_ram_size != gb->mbc_ram_size) {
    239             free(gb->mbc_ram);
    240             gb->mbc_ram = NULL;
    241         }
    242         
    243         if (gb->mbc_ram_size && !gb->mbc_ram) {
    244             gb->mbc_ram = malloc(gb->mbc_ram_size);
    245             /* Todo: Some games assume unintialized MBC RAM is 0xFF. It this true for all cartridge types? */
    246             memset(gb->mbc_ram, 0xFF, gb->mbc_ram_size);
    247         }
    248     }
    249 
    250     /* MBC1 has at least 3 types of wiring (We currently support two (Standard and 4bit-MBC1M) of these).
    251        See http://forums.nesdev.com/viewtopic.php?f=20&t=14099 */
    252 
    253     /* Attempt to "guess" wiring */
    254     if (gb->cartridge_type->mbc_type == GB_MBC1) {
    255         if (gb->rom_size >= 0x44000 && memcmp(gb->rom + 0x104, gb->rom + 0x40104, 0x30) == 0) {
    256             gb->mbc1_wiring = GB_MBC1M_WIRING;
    257         }
    258     }
    259     
    260     /* Detect MBC30 */
    261     if (gb->cartridge_type->mbc_type == GB_MBC3) {
    262         if (gb->rom_size > 0x200000 || gb->mbc_ram_size > 0x8000) {
    263             gb->is_mbc30 = true;
    264         }
    265     }
    266     
    267     GB_reset_mbc(gb);
    268 }
    269 
    270 void GB_reset_mbc(GB_gameboy_t *gb)
    271 {
    272     gb->mbc_rom0_bank = 0;
    273     if (gb->cartridge_type->mbc_type == GB_MMM01) {
    274         gb->mbc_rom_bank = -1;
    275         gb->mbc_rom0_bank = -2;
    276         gb->mmm01.ram_bank_mask = -1;
    277     }
    278     else if (gb->cartridge_type->mbc_type == GB_MBC5 ||
    279              gb->cartridge_type->mbc_type == GB_CAMERA) {
    280         gb->mbc5.rom_bank_low = 1;
    281         gb->mbc_rom_bank = 1;
    282     }
    283     else if (gb->cartridge_type->mbc_type == GB_MBC7) {
    284         gb->mbc7.x_latch = gb->mbc7.y_latch = 0x8000;
    285         gb->mbc7.latch_ready = true;
    286         gb->mbc7.read_bits = -1;
    287         gb->mbc7.eeprom_do = true;
    288     }
    289     else {
    290         gb->mbc_rom_bank = 1;
    291     }
    292 }

This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.