| @@ -53,26 +53,6 @@ int nes_cart_load_mem(void* data, int size, nes* sys) { | |||
| } | |||
| if (0 == status) { | |||
| // PPU Bank 0 - 7: Default pattern tables | |||
| for (int page = 0; page < 8; ++page) { | |||
| mem->ppu.bank[page] = chr_page(&mem->ppu, page); | |||
| } | |||
| // PPU Bank 8 - 11: Mirrored VRAM | |||
| nes_ppu_set_mirroring( | |||
| &mem->ppu, hdr->flags_6 & ines_Flag_Vert_Mirror | |||
| ); | |||
| // PPU Bank 12 - 15: Unused & Palette | |||
| for (int page = 12; page < 16; ++page) { | |||
| mem->ppu.bank[page] = mem->ppu.pal_bank; | |||
| } | |||
| // Map the SRAM bank to battery-backed SRAM if present | |||
| if (hdr->flags_6 & ines_Flag_Battery) { | |||
| mem->sram_bank = mem->sram; | |||
| } | |||
| int i_mapper = (hdr->flags_7 & ines_Mapper_Nibble_Hi) | | |||
| ((hdr->flags_6 & ines_Mapper_Nibble_Lo) >> 4); | |||
| const nes_Mapper *mapper = nes_mappers[i_mapper]; | |||
| @@ -80,9 +60,48 @@ int nes_cart_load_mem(void* data, int size, nes* sys) { | |||
| LOGE("Mapper %d not supported", i_mapper); | |||
| status = -1; | |||
| } else { | |||
| LOGI("Mapper %d", i_mapper); | |||
| // Set up CHR RAM if used | |||
| if (hdr->chr_size_lsb <= 0) { | |||
| int chr_size = mapper->max_chr_banks * | |||
| NES_CHR_ROM_PAGE_SIZE; | |||
| mem->ppu.chr_ram = calloc(1, chr_size); | |||
| LOGI("CHR RAM: %d kB", chr_size / 1024); | |||
| mem->ppu.chr = mem->ppu.chr_ram; | |||
| mem->ppu.n_chr_banks = chr_size / | |||
| NES_CHR_ROM_PAGE_SIZE; | |||
| } | |||
| LOGI("Mapper %d: %s", i_mapper, mapper->name); | |||
| mem->mapper = *mapper; | |||
| status = mem->mapper.init(&mem->mapper, hdr, mem); | |||
| mem->mapper.data = calloc(1, mapper->data_size); | |||
| if (NULL == mem->mapper.data) { | |||
| LOGE("Failed to allocate mapper data"); | |||
| status = -1; | |||
| } else { | |||
| // PPU Bank 0 - 7: Default pattern tables | |||
| for (int page = 0; page < 8; ++page) { | |||
| mem->ppu.bank[page] = chr_page(&mem->ppu, page); | |||
| } | |||
| // PPU Bank 8 - 11: Mirrored VRAM | |||
| nes_ppu_set_mirroring( | |||
| &mem->ppu, hdr->flags_6 & ines_Flag_Vert_Mirror | |||
| ); | |||
| // PPU Bank 12 - 15: Unused & Palette | |||
| for (int page = 12; page < 16; ++page) { | |||
| mem->ppu.bank[page] = mem->ppu.pal_bank; | |||
| } | |||
| // Map the SRAM bank to battery-backed SRAM if present | |||
| if (hdr->flags_6 & ines_Flag_Battery) { | |||
| mem->sram_bank = mem->sram; | |||
| } | |||
| status = mem->mapper.init(&mem->mapper, | |||
| hdr, mem); | |||
| } | |||
| // TODO: Deallocate mapper data when we're done | |||
| } | |||
| } | |||
| @@ -22,5 +22,6 @@ static int map000_init(nes_Mapper* map, const ines_Header* hdr, | |||
| const nes_Mapper map000 = { | |||
| .name = "NROM", | |||
| .max_chr_banks = 8, | |||
| .data_size = 0, | |||
| .init = map000_init, | |||
| }; | |||
| @@ -1,5 +1,3 @@ | |||
| #include <stdlib.h> | |||
| #include "memory.h" | |||
| //#define DEBUG "MMC1" | |||
| @@ -90,16 +88,8 @@ static void map001_reset(nes_Mapper* map, nes_Memory* mem) { | |||
| static int map001_init(nes_Mapper* map, const ines_Header* hdr, | |||
| nes_Memory* mem) { | |||
| int status = -1; | |||
| map->data = calloc(1, sizeof(map001_data)); | |||
| if (NULL != map->data) { | |||
| map001_reset(map, mem); | |||
| status = 0; | |||
| } | |||
| return status; | |||
| map001_reset(map, mem); | |||
| return 0; | |||
| } | |||
| static int map001_write_rom(nes_Mapper* map, nes_Memory* mem, | |||
| @@ -156,6 +146,7 @@ static int map001_write_rom(nes_Mapper* map, nes_Memory* mem, | |||
| const nes_Mapper map001 = { | |||
| .name = "MMC1", | |||
| .max_chr_banks = 128, | |||
| .data_size = sizeof(map001_data), | |||
| .init = map001_init, | |||
| .reset = map001_reset, | |||
| .write_rom = map001_write_rom, | |||
| @@ -0,0 +1,44 @@ | |||
| #include "memory.h" | |||
| typedef struct { | |||
| uint8_t bank; | |||
| } map002_data; | |||
| static inline void uxrom_set_bank(map002_data* data, | |||
| nes_Memory* mem, | |||
| uint8_t bank) { | |||
| data->bank = (bank % (mem->n_rom_banks / 2)); | |||
| mem->rom_bank[0] = prg_rom_page(mem, (data->bank * 2) + 0); | |||
| mem->rom_bank[1] = prg_rom_page(mem, (data->bank * 2) + 1); | |||
| } | |||
| static void map002_reset(nes_Mapper* map, nes_Memory* mem) { | |||
| uxrom_set_bank((map002_data*)map->data, mem, 0); | |||
| } | |||
| static int map002_init(nes_Mapper* map, const ines_Header* hdr, | |||
| nes_Memory* mem) { | |||
| int last_bank = (mem->n_rom_banks / 2) - 1; | |||
| mem->rom_bank[2] = prg_rom_page(mem, (last_bank * 2) + 0); | |||
| mem->rom_bank[3] = prg_rom_page(mem, (last_bank * 2) + 1); | |||
| map002_reset(map, mem); | |||
| return 0; | |||
| } | |||
| static int map002_write_rom(nes_Mapper* map, nes_Memory* mem, | |||
| uint16_t addr, uint8_t val) { | |||
| uxrom_set_bank((map002_data*)map->data, mem, val); | |||
| return 0; | |||
| } | |||
| const nes_Mapper map002 = { | |||
| .name = "UxROM", | |||
| .max_chr_banks = 8, | |||
| .data_size = sizeof(map002_data), | |||
| .init = map002_init, | |||
| .reset = map002_reset, | |||
| .write_rom = map002_write_rom, | |||
| }; | |||
| @@ -3,8 +3,10 @@ | |||
| extern const nes_Mapper map000; | |||
| extern const nes_Mapper map001; | |||
| extern const nes_Mapper map002; | |||
| const nes_Mapper* nes_mappers[256] = { | |||
| &map000, | |||
| &map001, | |||
| &map002, | |||
| }; | |||
| @@ -11,6 +11,8 @@ struct nes_Memory; | |||
| struct nes_Mapper { | |||
| const char* name; | |||
| int max_chr_banks; | |||
| int data_size; | |||
| void* data; | |||
| int (*init)(struct nes_Mapper*, const ines_Header*, struct nes_Memory*); | |||
| void (*done)(struct nes_Mapper*); | |||
| @@ -26,8 +28,6 @@ struct nes_Mapper { | |||
| void (*ppu_bus)(struct nes_Mapper*, uint16_t addr); | |||
| void (*ppu_mem_mode)(struct nes_Mapper*, uint8_t mode); | |||
| void* data; | |||
| }; | |||
| typedef struct nes_Mapper nes_Mapper; | |||