diff --git a/NES Emulator/CMakeLists.txt b/NES Emulator/CMakeLists.txt index 550b3df..62f51c0 100644 --- a/NES Emulator/CMakeLists.txt +++ b/NES Emulator/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required (VERSION 3.8) -add_executable (nesemu "main.c" "bus.c" "cpu.c" "cartridge.c" "opcodes.c" "log.c" "ppu.c" "mappers/mapper000.c" "mapper.c" "controller.c") +add_executable (nesemu "main.c" "bus.c" "cpu.c" "cartridge.c" "opcodes.c" "log.c" "ppu.c" "mappers/mapper000.c" "mapper.c" "controller.c" "mappers/mapper001.h" "mappers/mapper001.c") target_include_directories(nesemu PUBLIC ${SDL2_INCLUDE_DIRS}) target_link_libraries(nesemu PUBLIC ${SDL2_LIBRARIES}) diff --git a/NES Emulator/bus.c b/NES Emulator/bus.c index ccc4784..afe7fe3 100644 --- a/NES Emulator/bus.c +++ b/NES Emulator/bus.c @@ -34,7 +34,7 @@ struct Bus* createBus(SDL_Renderer* renderer) memset(bus->ram, 0x00, 0x18); // Create and insert cartridge - bus->cartridge = createCartridge(bus, "roms/donkeykong.nes"); + bus->cartridge = createCartridge(bus, "roms/nestest.nes"); // Create CPU and attach it bus->cpu = createCPU(bus); diff --git a/NES Emulator/mapper.c b/NES Emulator/mapper.c index 9174f40..ddb459e 100644 --- a/NES Emulator/mapper.c +++ b/NES Emulator/mapper.c @@ -4,6 +4,7 @@ #include #include "mappers/mapper000.h" +#include "mappers/mapper001.h" struct Mapper* createMapper(Byte id, Byte prg_rom_size, Byte chr_rom_size, FILE* fp) { @@ -30,6 +31,18 @@ struct Mapper* createMapper(Byte id, Byte prg_rom_size, Byte chr_rom_size, FILE* mapper->get_pattern_table_texture = &Mapper000_GetPatternTableTexture; } break; + case 1: + { + struct Mapper001* mp = createMapper001(prg_rom_size, chr_rom_size, fp); + mapper->mapperStruct = (void*)mp; + + mapper->read_cpu = &Mapper001_ReadCPU; + mapper->read_ppu = &Mapper001_ReadPPU; + mapper->write_cpu = &Mapper001_WriteCPU; + mapper->write_ppu = &Mapper001_WritePPU; + mapper->get_pattern_table_texture = &Mapper001_GetPatternTableTexture; + } break; + default: fprintf(stderr, "Mapper with ID %d is not implemented\n", id); exit(-1); diff --git a/NES Emulator/mappers/mapper001.c b/NES Emulator/mappers/mapper001.c new file mode 100644 index 0000000..be3278d --- /dev/null +++ b/NES Emulator/mappers/mapper001.c @@ -0,0 +1,175 @@ +#include "mapper001.h" + +struct Mapper001* createMapper001(Byte prg_rom_size, Byte chr_rom_size, FILE* fp) +{ + struct Mapper001* mapper = (struct Mapper001*)malloc(sizeof(struct Mapper001)); + if (mapper == NULL) + { + fprintf(stderr, "Failed to create Mapper 000. Aborting\n"); + exit(-1); + } + + mapper->prg_rom_size = prg_rom_size; + mapper->prg_rom = (Byte*)calloc(prg_rom_size, 0x4000); + if (mapper->prg_rom == NULL) + { + fprintf(stderr, "Failed to allocate cartridge PRG ROM. Aborting\n"); + exit(-1); + } + + mapper->chr_rom_size = chr_rom_size; + mapper->chr_rom = (Byte*)calloc(chr_rom_size, 0x2000); + if (mapper->chr_rom == NULL) + { + fprintf(stderr, "Failed to allocate cartridge CHR ROM. Aborting\n"); + exit(-1); + } + + fread(mapper->prg_rom, 0x4000, prg_rom_size, fp); + fread(mapper->chr_rom, 0x2000, chr_rom_size, fp); + + mapper->lowerChrBank = mapper->chr_rom; + mapper->upperChrBank = mapper->chr_rom + 0x1000; + mapper->lowerPrgBank = mapper->prg_rom; + mapper->upperPrgBank = mapper->prg_rom + 0x4000; + + return mapper; +} + +void destroyMapper001(struct Mapper001* mapper) +{ + free(mapper->chr_rom); + free(mapper->prg_rom); + + free(mapper); +} + +Byte Mapper001_ReadCPU(void* mapper, Word address) +{ + struct Mapper001* sMapper = (struct Mapper000*)mapper; + Byte val = 0x00; + + if (address >= 0x6000 && address < 0x8000) + { + fprintf(stderr, "This Mapper 000 implementation doesnt support FamilyBasic PRG RAM\n"); + exit(-1); + } + else if (address >= 0x8000 && address <= 0xBFFF) + { + val = sMapper->lowerPrgBank[address & 0x3FFF]; + } + else if (address >= 0xC000 && address <= 0xFFFF) + { + val = sMapper->upperPrgBank[address & 0x3FFF]; + } + else + { + fprintf(stderr, "Cartridge read access violation by the CPU at $%04X\n", address); + exit(-1); + } + + return val; +} + +Byte Mapper001_ReadPPU(void* mapper, Word address) +{ + struct Mapper001* sMapper = (struct Mapper000*)mapper; + Byte val = 0x00; + + if (address >= 0x2000) + { + fprintf(stderr, "Cartridge read access violation by the PPU at $%04X\n", address); + exit(-1); + } + else if (address < 0x1000 + 0x1000 * (1 - sMapper->control.chr_bank_mode)) + { + val = sMapper->lowerChrBank[address]; + } + else + { + val = sMapper->upperChrBank[address & 0x0FFF]; + } + + return val; +} + +void Mapper001_WriteCPU(void* mapper, Word address, Byte value) +{ + struct Mapper001* sMapper = (struct Mapper000*)mapper; + if ((value & 0x80) == 0x80) + { + sMapper->shiftRegister = 0x10; + sMapper->control.raw |= 0x0C; + return; + } + + if ((sMapper->shiftRegister & 0x1) != 0x1) + { + sMapper->shiftRegister >>= 1; + sMapper->shiftRegister |= ((value & 0x1) << 4); + return; + } + + Byte valueToBeWritten = (sMapper->shiftRegister >> 1) | ((value & 0x1) << 4); + if (address >= 0x8000 && address <= 0x9FFF) + { + sMapper->control.raw = valueToBeWritten; + } + else if (address >= 0xA000 && address <= 0xBFFF) + { + if (sMapper->control.chr_bank_mode == 0x00) // 8KB mode + valueToBeWritten &= ~(0x1); // clear last bit + + sMapper->lowerChrBank = sMapper->chr_rom + (size_t)valueToBeWritten * 0x1000; + } + else if (address >= 0xC000 && address <= 0xDFFF) + { + if (sMapper->control.chr_bank_mode == 0x00) // 8KB mode + { + return; // ignore + } + + sMapper->upperChrBank = sMapper->chr_rom + (size_t)valueToBeWritten * 0x1000; + } + else + { + switch (sMapper->control.prg_bank_mode) + { + case 0: // 32KB mode + case 1: + valueToBeWritten &= ~(0x1); + sMapper->lowerPrgBank = sMapper->prg_rom + ((size_t)valueToBeWritten & 0xE) * 0x8000; + sMapper->upperPrgBank = sMapper->lowerPrgBank + 0x4000; + break; + + case 2: // fix first bank, switch second + sMapper->lowerPrgBank = sMapper->prg_rom; + sMapper->upperPrgBank = sMapper->prg_rom + ((size_t)valueToBeWritten & 0xE) * 0x4000; + break; + + case 3: // fix last bank, switch first + sMapper->lowerPrgBank = sMapper->prg_rom + ((size_t)valueToBeWritten & 0xE) * 0x4000; + sMapper->upperPrgBank = sMapper->prg_rom + ((size_t)sMapper->prg_rom_size - 1) * 0x4000; + break; + + } + } +} + + +void Mapper001_WritePPU(void* mapper, Word address, Byte value) +{ + // nothing +} + +void Mapper001_GetPatternTableTexture(void* mapper, SDL_Texture* texture, int index) +{ + struct Mapper001* sMapper = (struct Mapper001*)mapper; + Byte* selectedBank = ((index == 0) ? sMapper->lowerChrBank : sMapper->upperChrBank); + + int pitch; + void* pixels; + SDL_LockTexture(texture, NULL, &pixels, &pitch); + SDL_memcpy(pixels, selectedBank, 0x1000); + SDL_UnlockTexture(texture); +} diff --git a/NES Emulator/mappers/mapper001.h b/NES Emulator/mappers/mapper001.h new file mode 100644 index 0000000..e31aab8 --- /dev/null +++ b/NES Emulator/mappers/mapper001.h @@ -0,0 +1,50 @@ +#ifndef _MAPPER_001_ +#define _MAPPER_001_ + +#include +#include "../types.h" +#include "SDL.h" + +struct Mapper001 +{ + Byte prg_rom_size; + Byte chr_rom_size; + + Byte* prg_rom; + Byte* chr_rom; + + /////////////////////////// + /// REGISTERS /// + /////////////////////////// + Byte shiftRegister; + + union + { + struct + { + Byte mirroring : 2; + Byte prg_bank_mode : 2; + Byte chr_bank_mode : 1; + Byte padding : 3; + }; + + Byte raw; + } control; + + Byte* lowerChrBank; + Byte* upperChrBank; + Byte* lowerPrgBank; + Byte* upperPrgBank; +}; + +struct Mapper001* createMapper001(Byte prg_rom_size, Byte chr_rom_size, FILE* fp); +void destroyMapper001(struct Mapper001* mapper); + +Byte Mapper001_ReadCPU(void* mapper, Word address); +Byte Mapper001_ReadPPU(void* mapper, Word address); +void Mapper001_WriteCPU(void* mapper, Word address, Byte value); +void Mapper001_WritePPU(void* mapper, Word address, Byte value); + +void Mapper001_GetPatternTableTexture(void* mapper, SDL_Texture* texture, int index); + +#endif // _MAPPER_001_ diff --git a/roms/coredump.nes b/roms/coredump.nes new file mode 100644 index 0000000..779eda8 Binary files /dev/null and b/roms/coredump.nes differ