Fix memory mapping

This commit is contained in:
FyloZ 2024-05-23 23:52:04 -04:00
parent 07d044c47f
commit 66785039a9
Signed by: william
GPG Key ID: 835378AE9AF4AE97
16 changed files with 131 additions and 185 deletions

View File

@ -11,7 +11,7 @@ add_subdirectory(cpu)
add_subdirectory(ppu) add_subdirectory(ppu)
add_subdirectory(mappers) add_subdirectory(mappers)
add_subdirectory(rom) add_subdirectory(rom)
add_subdirectory(debugger) #add_subdirectory(debugger)
add_subdirectory(utils) add_subdirectory(utils)
add_subdirectory(gui) add_subdirectory(gui)
@ -29,7 +29,7 @@ set(SOURCE main.c system.c)
add_executable(nes_emulator ${HEADERS} ${SOURCE}) add_executable(nes_emulator ${HEADERS} ${SOURCE})
target_link_libraries(nes_emulator nes_cpu nes_ppu nes_mappers nes_rom nes_debugger nes_utils nes_gui log.c) target_link_libraries(nes_emulator nes_cpu nes_ppu nes_mappers nes_rom nes_utils nes_gui log.c)
target_include_directories(nes_emulator PUBLIC target_include_directories(nes_emulator PUBLIC
"${PROJECT_BINARY_DIR}" "${PROJECT_BINARY_DIR}"
${EXTRA_INCLUDES}) ${EXTRA_INCLUDES})

View File

@ -64,7 +64,7 @@ void cpu_process_nmi() {
} }
void oam_dma_upload() { void oam_dma_upload() {
byte page_high_addr = *ppu_get_state()->oam_dma_register; // TODO byte page_high_addr = ppu_get_state()->oam_dma_register; // TODO
address page_addr = ((address) page_high_addr) << 8; address page_addr = ((address) page_high_addr) << 8;
byte n = 0xff; byte n = 0xff;

View File

@ -8,62 +8,107 @@
#include "../include/rom.h" #include "../include/rom.h"
#include "cpu.h" #include "cpu.h"
#define RAM_SIZE 0x0800
#define RAM_MAX_ADDR 0x2000 #define RAM_MAX_ADDR 0x2000
#define RAM_BANK_SIZE 0x800
#define PPU_MAX_ADDR 0x4000 #define PPU_MAX_ADDR 0x4000
#define PPU_BANK_SIZE 0x8
#define APU_MAX_ADDR 0x4020 #define APU_MAX_ADDR 0x4020
#define MAX_ADDR 0xffff #define UNMAPPED_MAX_ADDR 0x6000
#define CARTRIDGE_RAM_MAX_ADDR 0x8000
#define MEM_ADDR_MAX 0xffff
byte ram[RAM_SIZE]; byte ram[RAM_SIZE];
byte mem_get_byte(address addr) { byte *mem_get_ptr(address addr) {
assert(addr <= MAX_ADDR); assert(addr <= RAM_MAX_ADDR);
if (addr >= RAM_MAX_ADDR && addr < PPU_MAX_ADDR) { if (addr < RAM_MAX_ADDR) {
byte reg = (addr - RAM_MAX_ADDR) % PPU_BANK_SIZE; address ram_addr = addr % RAM_SIZE; // There is four mirror of RAM
return ppu_read_reg(reg); return &ram[ram_addr];
} }
return ram[addr]; // Only supported for RAM
assert(false);
} }
byte *mem_get_ptr(address addr) { byte mem_get_byte(address addr) {
assert(addr <= MAX_ADDR); assert(addr <= MEM_ADDR_MAX);
return &ram[addr]; if (addr < RAM_MAX_ADDR) {
address ram_addr = addr % RAM_SIZE;
return ram[ram_addr];
} else if (addr < PPU_MAX_ADDR) {
address relative_addr = addr - RAM_MAX_ADDR;
byte ppu_reg = relative_addr % 8;
return ppu_read_reg(ppu_reg);
} else if (addr < APU_MAX_ADDR) {
// TODO NES API and I/O registers
return 0;
} else if (addr < UNMAPPED_MAX_ADDR) {
// Unmapped
assert(false);
} else if (addr < CARTRIDGE_RAM_MAX_ADDR) {
// TODO Cartridge RAM
return 0;
} else {
address rom_addr = addr - CARTRIDGE_RAM_MAX_ADDR;
Mapper *mapper = system_get_mapper();
return *(mapper->mem_read(rom_addr));
}
} }
word mem_get_word(address addr) { word mem_get_word(address addr) {
assert(addr < MAX_ADDR); assert(addr <= MEM_ADDR_MAX - 1);
word word = ram[addr]; byte data1;
word += ram[addr + 1] << 8; // Little endian byte data2;
return word;
if (addr < RAM_MAX_ADDR - 1) {
address ram_addr = addr % RAM_SIZE;
data1 = ram[ram_addr];
data2 = ram[ram_addr + 1];
} else if (addr < UNMAPPED_MAX_ADDR) {
// Unsupported
assert(false);
} else if (addr < CARTRIDGE_RAM_MAX_ADDR) {
// TODO Cartridge RAM
return 0;
} else {
address rom_addr = addr - CARTRIDGE_RAM_MAX_ADDR;
Mapper *mapper = system_get_mapper();
byte *location = mapper->mem_read(rom_addr);
data1 = *location;
data2 = *(location + 1);
}
return data1 + (data2 << 8);
} }
void mem_set_byte(address addr, byte byte) { void mem_set_byte(address addr, byte data) {
assert(addr < MAX_ADDR); assert(addr < MEM_ADDR_MAX);
log_trace("Writing '%02x' to address 0x%04x", byte, addr);
if (addr < RAM_MAX_ADDR) { if (addr < RAM_MAX_ADDR) {
address init_ram_addr = addr % RAM_BANK_SIZE; address ram_addr = addr % RAM_SIZE;
ram[ram_addr] = data;
// The value must also be cloned in the three mirrors
for (int i = 0; i < 4; i++) {
address ram_addr = init_ram_addr + RAM_BANK_SIZE * i;
ram[ram_addr] = byte;
}
} else if (addr < PPU_MAX_ADDR) { } else if (addr < PPU_MAX_ADDR) {
address reg_addr = (addr - RAM_MAX_ADDR) % PPU_BANK_SIZE; address relative_addr = addr - RAM_MAX_ADDR;
ppu_write_reg(reg_addr, byte); byte ppu_reg = relative_addr % 8;
ppu_write_reg(ppu_reg, data);
} else if (addr == PPU_REGISTER_OAM_DMA_ADDR) {
// Writing to this address triggers an upload to the PPU memory
cpu_trigger_oam_dma();
} else if (addr < APU_MAX_ADDR) {
// TODO NES API and I/O registers
} else if (addr < UNMAPPED_MAX_ADDR) {
// Unmapped
assert(false);
} else if (addr < CARTRIDGE_RAM_MAX_ADDR) {
// TODO Cartridge RAM
} else { } else {
ram[addr] = byte; // ROM is read-only
assert(false);
if (addr == PPU_REGISTER_OAM_DMA_ADDR) {
// Writing to this address triggers an upload to the PPU memory
cpu_trigger_oam_dma();
}
} }
} }

View File

@ -37,8 +37,8 @@ word mem_get_word(address addr);
* Sets a byte in a system's memory. * Sets a byte in a system's memory.
* *
* @param addr The address to set * @param addr The address to set
* @param value The value to set * @param data The data to set
*/ */
void mem_set_byte(address addr, byte value); void mem_set_byte(address addr, byte data);
#endif //NESEMULATOR_MEMORY_H #endif //NESEMULATOR_MEMORY_H

View File

@ -5,7 +5,6 @@
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#include "types.h" #include "types.h"
#include "../ppu/memory.h"
#ifndef NESEMULATOR_PPU_H #ifndef NESEMULATOR_PPU_H
#define NESEMULATOR_PPU_H #define NESEMULATOR_PPU_H
@ -61,11 +60,18 @@ typedef struct ppu_memory {
byte *palette; byte *palette;
} PPUMemory; } PPUMemory;
typedef struct ppu_tile_fetch {
byte nametable;
byte attribute_table;
byte pattern_table_tile_low;
byte pattern_table_tile_high;
} PPUTileFetch;
typedef struct ppu { typedef struct ppu {
PPUMemory memory; PPUMemory memory;
byte *registers; byte registers[8];
byte *oam_dma_register; byte oam_dma_register;
byte vram[PPU_VRAM_SIZE]; byte vram[PPU_VRAM_SIZE];
byte oam[PPU_OAM_SIZE]; byte oam[PPU_OAM_SIZE];
bool odd_frame; bool odd_frame;
@ -90,8 +96,7 @@ PPU *ppu_get_state();
* *
* @param ppu * @param ppu
*/ */
void ppu_init(byte *registers_ram, byte *oam_dma_register); void ppu_init();
void ppu_uninit();
/** /**
* Cycles the PPU. * Cycles the PPU.

View File

@ -19,7 +19,7 @@ typedef struct {
bool nametable_mirrored; bool nametable_mirrored;
byte *prg_rom; byte *prg_rom;
int prg_rom_size; unsigned int prg_rom_size;
byte *chr_rom; byte *chr_rom;
} Rom; } Rom;

View File

@ -5,14 +5,14 @@
#ifndef NESEMULATOR_TYPES_H #ifndef NESEMULATOR_TYPES_H
#define NESEMULATOR_TYPES_H #define NESEMULATOR_TYPES_H
#define RAM_SIZE 0xffff //#define RAM_SIZE 0xffff
#define VRAM_SIZE 0x4000 //#define VRAM_SIZE 0x4000
typedef unsigned char byte; typedef unsigned char byte;
typedef unsigned short address; typedef unsigned short address;
typedef unsigned short word; typedef unsigned short word;
//typedef byte ram[RAM_SIZE]; //typedef byte ram[RAM_SIZE];
typedef byte vram[VRAM_SIZE]; //typedef byte vram[VRAM_SIZE];
#endif //NESEMULATOR_TYPES_H #endif //NESEMULATOR_TYPES_H

2
main.c
View File

@ -27,7 +27,7 @@ int main() {
log_set_level(LOG_INFO); log_set_level(LOG_INFO);
system_init(); system_init();
char *rom_path = "../test_roms/dk_japan.nes"; char *rom_path = "../test_roms/dk_jp.nes";
if (!rom_load(rom_path)) { if (!rom_load(rom_path)) {
system_uninit(); system_uninit();

View File

@ -2,23 +2,17 @@
#include "../include/rom.h" #include "../include/rom.h"
#include "../cpu/memory.h" #include "../cpu/memory.h"
#define SIMPLE_MAPPER_PRG_START_ADDR 0x8000
#define PRG_BANK_SIZE 0x4000 // 16Kb #define PRG_BANK_SIZE 0x4000 // 16Kb
byte *nrom_mem_read(address addr) { byte *nrom_mem_read(address addr) {
if (addr >= SIMPLE_MAPPER_PRG_START_ADDR) { Rom *rom = rom_get();
Rom *rom = rom_get();
address relative_addr = addr - SIMPLE_MAPPER_PRG_START_ADDR;
if (addr < PRG_BANK_SIZE || rom->prg_rom_size > PRG_BANK_SIZE) { if (addr < PRG_BANK_SIZE || rom->prg_rom_size > PRG_BANK_SIZE) {
return &rom->prg_rom[relative_addr]; return &rom->prg_rom[addr];
}
// The second bank is mirrored
return &rom->prg_rom[relative_addr - PRG_BANK_SIZE];
} }
return NULL; // The second bank is mirrored
return &rom->prg_rom[addr - PRG_BANK_SIZE];
} }
byte *nrom_ppu_read(address addr) { byte *nrom_ppu_read(address addr) {

View File

@ -1,7 +1,5 @@
set(HEADERS pattern_table.h ppu.h palette.h set(HEADERS pattern_table.h ppu.h palette.h)
memory.h) set(SOURCE pattern_table.c ppu.c palette.c)
set(SOURCE pattern_table.c ppu.c palette.c
memory.c)
add_library(nes_ppu ${SOURCE} ${HEADERS}) add_library(nes_ppu ${SOURCE} ${HEADERS})

View File

@ -1,48 +0,0 @@
//
// Created by william on 5/17/24.
//
#include <assert.h>
#include "memory.h"
void ppu_vram_fetch(PPUVramFetch *fetch, address addr) {
assert(addr < VRAM_SIZE);
if (fetch->finished) {
fetch->finished = false;
return;
}
fetch->data = *fetch->vram[addr];
fetch->finished = true;
}
void ppu_tile_fetch(PPUTileFetch *fetch, address addr) {
if (fetch->fetch_cycle >= 8) {
fetch->fetch_cycle = 0;
}
if (fetch->fetch_cycle % 2 == 0) {
// First cycle of a memory fetch
ppu_vram_fetch(&fetch->vram_fetch, addr + (fetch->fetch_cycle / 2));
} else {
// Second cycle of a fetch, the data should be available
byte data = fetch->vram_fetch.data;
switch (fetch->fetch_cycle) {
case 1:
fetch->nametable = data;
break;
case 3:
fetch->attribute_table = data;
break;
case 5:
fetch->pattern_table_tile_low = data;
break;
case 7:
fetch->pattern_table_tile_high = data;
break;
default:
assert(false);
}
}
}

View File

@ -1,28 +0,0 @@
//
// Created by william on 5/17/24.
//
#include <stdbool.h>
#include "../include/types.h"
#ifndef NES_EMULATOR_MEMORY_H
#define NES_EMULATOR_MEMORY_H
typedef struct ppu_vram_fetch {
vram *vram;
byte data;
bool finished;
} PPUVramFetch;
typedef struct ppu_tile_fetch {
byte nametable;
byte attribute_table;
byte pattern_table_tile_low;
byte pattern_table_tile_high;
} PPUTileFetch;
void ppu_vram_fetch(PPUVramFetch *fetch, address addr);
void ppu_tile_fetch(PPUTileFetch *fetch, address addr);
#endif //NES_EMULATOR_MEMORY_H

View File

@ -29,12 +29,8 @@
PPU ppu_state; PPU ppu_state;
void ppu_init(byte *registers_ram, byte *oam_dma_register) { void ppu_init() {
memset(&ppu_state, 0, sizeof(PPU)); memset(&ppu_state, 0, sizeof(PPU));
ppu_state.oam_dma_register = oam_dma_register;
ppu_state.registers = registers_ram;
memset(&ppu_state.registers, 0, 8);
} }
PPU *ppu_get_state() { PPU *ppu_get_state() {
@ -62,24 +58,24 @@ void ppu_visible_frame(unsigned int cycle) {
if (cycle == 0) { if (cycle == 0) {
// Idle... // Idle...
} else if (cycle <= 256) { } else if (cycle <= 256) {
byte tile_fetch_cycle = (cycle - 1) % 8; // byte tile_fetch_cycle = (cycle - 1) % 8;
switch (tile_fetch_cycle) { // switch (tile_fetch_cycle) {
case 1: // case 1:
ppu_state.tile_fetch.nametable = ppu_read(ppu_state.ppu_address); // ppu_state.tile_fetch.nametable = ppu_read(ppu_state.ppu_address);
break; // break;
case 3: // case 3:
ppu_state.tile_fetch.attribute_table = ppu_read(ppu_state.ppu_address); // ppu_state.tile_fetch.attribute_table = ppu_read(ppu_state.ppu_address);
break; // break;
case 5: // case 5:
ppu_state.tile_fetch.pattern_table_tile_low = ppu_read(ppu_state.ppu_address); // ppu_state.tile_fetch.pattern_table_tile_low = ppu_read(ppu_state.ppu_address);
break; // break;
case 7: // case 7:
ppu_state.tile_fetch.pattern_table_tile_high = ppu_read(ppu_state.ppu_address); // ppu_state.tile_fetch.pattern_table_tile_high = ppu_read(ppu_state.ppu_address);
ppu_state.ppu_address++; // ppu_state.ppu_address++;
break; // break;
default: // default:
break; // break;
} // }
} else if (cycle <= 320) { } else if (cycle <= 320) {
// OAMADDR is cleared on sprite loading for pre-render and visible lines // OAMADDR is cleared on sprite loading for pre-render and visible lines
ppu_write_reg(PPU_REGISTER_OAM_ADDR, 0); ppu_write_reg(PPU_REGISTER_OAM_ADDR, 0);
@ -180,15 +176,6 @@ byte ppu_read_reg(byte reg) {
return ppu_state.registers[reg]; return ppu_state.registers[reg];
} }
void ppu_write_reg_ram(byte reg, byte data) {
byte *ppu_ram = mem_get_ptr(PPU_RAM_BASE_ADDR);
for (int i = 0; i < PPU_RAM_BANK_COUNT; i++) {
byte ram_offset = (i * PPU_RAM_BANK_SIZE) + reg;
*(ppu_ram + ram_offset) = data;
}
}
void ppu_write_reg(byte reg, byte data) { void ppu_write_reg(byte reg, byte data) {
ppu_state.registers[reg] = data; ppu_state.registers[reg] = data;
@ -234,7 +221,7 @@ void ppu_write_reg(byte reg, byte data) {
ppu_write_reg(PPU_REGISTER_OAM_ADDR, oam_addr + 1); ppu_write_reg(PPU_REGISTER_OAM_ADDR, oam_addr + 1);
} }
ppu_write_reg_ram(reg, data); ppu_state.registers[reg] = data;
} }
byte ppu_read(address addr) { byte ppu_read(address addr) {

View File

@ -59,7 +59,7 @@ typedef struct {
struct INesHeaderFlags flags; struct INesHeaderFlags flags;
} INesHeader; } INesHeader;
bool rom_is_ines(const char header[16]) { bool rom_is_ines(const byte header[16]) {
return header[0] == 'N' && header[1] == 'E' && header[2] == 'S'; return header[0] == 'N' && header[1] == 'E' && header[2] == 'S';
} }
@ -139,8 +139,6 @@ bool rom_ines_read_prg_rom(FILE *file, INesHeader *header, Rom *rom) {
return false; return false;
} }
system_get_mapper()->post_prg_load(header->prg_rom_size);
return true; return true;
} }

View File

@ -21,14 +21,13 @@ bool rom_load(char *path) {
return false; return false;
} }
char header_buffer[ROM_HEADER_SIZE] = {0}; size_t read_size = fread(&rom.header, sizeof(byte), ROM_HEADER_SIZE, file);
size_t read_size = fread(header_buffer, sizeof(char), ROM_HEADER_SIZE, file);
if (read_size < ROM_HEADER_SIZE) { if (read_size < ROM_HEADER_SIZE) {
log_error("Failed to read ROM"); log_error("Failed to read ROM");
return false; return false;
} }
if (!rom_is_ines(header_buffer)) { if (!rom_is_ines(rom.header)) {
log_error("Only iNes ROMs are supported"); log_error("Only iNes ROMs are supported");
return false; return false;
} }

View File

@ -13,11 +13,8 @@
System current_sys; System current_sys;
void system_init() { void system_init() {
byte *registers_base_addr = mem_get_ptr(PPU_REGISTERS_BASE_ADDR);
byte *oam_dma_register = mem_get_ptr(PPU_REGISTER_OAM_DMA_ADDR);
cpu_init(); cpu_init();
ppu_init(registers_base_addr, oam_dma_register); ppu_init();
current_sys.mapper = get_mapper(MAPPER_TYPE_SIMPLE); current_sys.mapper = get_mapper(MAPPER_TYPE_SIMPLE);
current_sys.cycle_count = 7; current_sys.cycle_count = 7;
@ -41,7 +38,6 @@ void system_next_frame() {
} }
void system_uninit() { void system_uninit() {
ppu_uninit();
} }
unsigned int system_get_cycles() { unsigned int system_get_cycles() {