diff --git a/cpu/cpu.c b/cpu/cpu.c index a1d5765..c1a0b02 100644 --- a/cpu/cpu.c +++ b/cpu/cpu.c @@ -42,7 +42,7 @@ void cpu_init() { void print_registers(byte op, unsigned long cycle_count) { log_debug("%#02x %#02x %s \t A:%#02x X:%#02x Y:%#02x F:%#02x SP:%#02x \t [%d]", - cpu_state.program_counter, + cpu_state.program_counter - 1, // The PC as been incremented when printing op, get_op_code_name(op), cpu_state.accumulator, diff --git a/cpu/memory.c b/cpu/memory.c index b7b93fa..b338995 100644 --- a/cpu/memory.c +++ b/cpu/memory.c @@ -98,6 +98,8 @@ void mem_set_byte(address addr, byte data) { byte ppu_reg = relative_addr % 8; ppu_write_reg(ppu_reg, data); } else if (addr == PPU_REGISTER_OAM_DMA_ADDR) { + ppu_write_reg_oam_addr(data); + // Writing to this address triggers an upload to the PPU memory cpu_trigger_oam_dma(); } else if (addr < APU_MAX_ADDR) { diff --git a/include/ppu.h b/include/ppu.h index 26eb7e1..7582712 100644 --- a/include/ppu.h +++ b/include/ppu.h @@ -53,11 +53,14 @@ #define PPU_MASK_NONE 0xff #define PATTERN_TABLE_SIZE 0x1000 +#define NAMETABLE_SIZE 0x0400 +#define PALETTE_TABLE_SIZE 0x0020 typedef struct ppu_memory { - byte *nametable_0; - byte *nametable_1; - byte *palette; + byte vram[PPU_VRAM_SIZE]; + byte nametable_0[NAMETABLE_SIZE]; + byte nametable_1[NAMETABLE_SIZE]; + byte palette[PALETTE_TABLE_SIZE]; } PPUMemory; typedef struct ppu_tile_fetch { @@ -72,7 +75,6 @@ typedef struct ppu { byte registers[8]; byte oam_dma_register; - byte vram[PPU_VRAM_SIZE]; byte oam[PPU_OAM_SIZE]; bool odd_frame; address v; @@ -83,6 +85,7 @@ typedef struct ppu { address ppu_address; PPUTileFetch tile_fetch; + PPUTileFetch next_tile_fetch; unsigned long frame; unsigned int scanline; unsigned int cycle; @@ -129,4 +132,8 @@ byte ppu_read_reg(byte reg); void ppu_write_reg(byte reg, byte data); +void ppu_write_reg_oam_addr(byte data); + +void ppu_write(address addr, byte data); + #endif //NESEMULATOR_PPU_H diff --git a/main.c b/main.c index 4b94fb5..2601972 100644 --- a/main.c +++ b/main.c @@ -17,7 +17,6 @@ */ #include #include "log.h" -#include "debugger/debugger.h" #include "include/rom.h" #include "include/system.h" @@ -27,7 +26,7 @@ int main() { log_set_level(LOG_INFO); system_init(); - char *rom_path = "../test_roms/dk_jp.nes"; + char *rom_path = "../test_roms/dk_japan.nes"; if (!rom_load(rom_path)) { system_uninit(); diff --git a/ppu/ppu.c b/ppu/ppu.c index 7dc0809..cc39fb8 100644 --- a/ppu/ppu.c +++ b/ppu/ppu.c @@ -19,6 +19,8 @@ #include "../include/ppu.h" #include "../cpu/cpu.h" #include "../include/rom.h" +#include "../gui/gui.h" +#include "pattern_table.h" #define PPU_VISIBLE_FRAME_END 240 #define PPU_POST_RENDER_LINE_START PPU_VISIBLE_FRAME_END @@ -54,28 +56,69 @@ void ppu_trigger_vbl_nmi() { cpu_trigger_nmi(); } +void ppu_draw_tile() { + PPUTileFetch tile_fetch = ppu_state.tile_fetch; + Canvas *canvas = gui_get_canvas(WINDOW_ID_MAIN); + + byte tile_fine_x = ppu_state.cycle % 8; + + byte bitmask = 1 << (PATTERN_TILE_SIZE - tile_fine_x - 1); + byte p1_byte = tile_fetch.pattern_table_tile_low & bitmask; + byte p2_byte = tile_fetch.pattern_table_tile_high & bitmask; + + Pixel pixel; + if (p1_byte && p2_byte) { + pixel.r = 255; + pixel.g = 255; + pixel.b = 255; + } else if (p2_byte) { + pixel.r = 255; + pixel.g = 0; + pixel.b = 0; + } else if (p1_byte) { + pixel.r = 0; + pixel.g = 255; + pixel.b = 255; + } else { + pixel.r = 0; + pixel.g = 0; + pixel.b = 0; + } + + canvas_draw_pos(canvas, pixel, ppu_state.cycle, ppu_state.scanline); +} + void ppu_visible_frame(unsigned int cycle) { if (cycle == 0) { // Idle... } else if (cycle <= 256) { -// byte tile_fetch_cycle = (cycle - 1) % 8; -// switch (tile_fetch_cycle) { -// case 1: -// ppu_state.tile_fetch.nametable = ppu_read(ppu_state.ppu_address); -// break; -// case 3: -// ppu_state.tile_fetch.attribute_table = ppu_read(ppu_state.ppu_address); -// break; -// case 5: -// ppu_state.tile_fetch.pattern_table_tile_low = ppu_read(ppu_state.ppu_address); -// break; -// case 7: -// ppu_state.tile_fetch.pattern_table_tile_high = ppu_read(ppu_state.ppu_address); -// ppu_state.ppu_address++; -// break; -// default: -// break; -// } + if (!ppu_read_flag(PPU_REGISTER_MASK, PPU_MASK_SHOW_BG) && ppu_state.scanline < 240) { + if (cycle <= 248) { + ppu_draw_tile(); + } + + byte tile_fetch_cycle = (cycle - 1) % 8; + switch (tile_fetch_cycle) { + case 1: + address nt_addr = 0x2000 + (ppu_state.scanline * 16) + (ppu_state.cycle / 8); + ppu_state.next_tile_fetch.nametable = ppu_read(nt_addr); + break; + case 3: + address at_addr = 0x23c0 + (ppu_state.cycle % 8); + ppu_state.next_tile_fetch.attribute_table = ppu_read(at_addr); + ppu_state.ppu_address++; + break; + case 5: + ppu_state.next_tile_fetch.pattern_table_tile_low = ppu_read(0x0000); + break; + case 7: + ppu_state.next_tile_fetch.pattern_table_tile_high = ppu_read(0x0008); + ppu_state.tile_fetch = ppu_state.next_tile_fetch; + break; + default: + break; + } + } } else if (cycle <= 320) { // OAMADDR is cleared on sprite loading for pre-render and visible lines ppu_write_reg(PPU_REGISTER_OAM_ADDR, 0); @@ -146,14 +189,16 @@ byte ppu_read_reg(byte reg) { if (reg == PPU_REGISTER_STATUS) { ppu_state.w = false; + byte status = ppu_state.registers[PPU_REGISTER_STATUS]; ppu_state.registers[PPU_REGISTER_STATUS] &= ~PPU_STATUS_VBLANK; + return status; } if (reg == PPU_REGISTER_DATA) { // Access to VRAM memory is slow, so reading it a first time generally return the memory at the previous address. // So we get the data first, then update the register. byte data = ppu_state.registers[reg]; - ppu_state.registers[reg] = ppu_state.vram[ppu_state.ppu_address]; + ppu_state.registers[reg] = ppu_state.memory.vram[ppu_state.ppu_address]; if (ppu_state.ppu_address > 0x3eff) { // But the palette data is returned immediately data = ppu_state.registers[reg]; @@ -205,7 +250,7 @@ void ppu_write_reg(byte reg, byte data) { } ppu_state.ppu_address = addr; } else if (reg == PPU_REGISTER_DATA) { - ppu_state.vram[ppu_state.ppu_address] = data; + ppu_write(ppu_state.ppu_address, data); byte increment = 1; if (ppu_read_flag(PPU_REGISTER_CTRL, PPU_CTRL_VRAM_ADDR_INCREMENT)) { @@ -224,6 +269,51 @@ void ppu_write_reg(byte reg, byte data) { ppu_state.registers[reg] = data; } +void ppu_write_reg_oam_addr(byte data) { + ppu_state.oam_dma_register = data; +} + +void ppu_write(address addr, byte data) { + assert(addr < PPU_VRAM_SIZE); + + address relative_addr; + + if (addr < 0x2000) { + // TODO Unsupported ? + } else if (addr < 0x2400) { + relative_addr = addr - 0x2000; + ppu_state.memory.nametable_0[relative_addr] = data; + } else if (addr < 0x2800) { + relative_addr = addr - 0x2400; + byte *nametable; + + if (rom_get()->nametable_mirrored) { + nametable = ppu_state.memory.nametable_1; + } else { + nametable = ppu_state.memory.nametable_0; + } + + nametable[relative_addr] = data; + } else if (addr < 0x2c00) { + relative_addr = addr - 0x2800; + byte *nametable; + + if (rom_get()->nametable_mirrored) { + nametable = ppu_state.memory.nametable_0; + } else { + nametable = ppu_state.memory.nametable_1; + } + + nametable[relative_addr] = data; + } else if (addr < 0x3000) { + relative_addr = addr - 0x2c00; + ppu_state.memory.nametable_1[relative_addr] = data; + } else if (addr >= 0x3f00) { + relative_addr = (addr - 0x3f00) % PALETTE_TABLE_SIZE; + ppu_state.memory.palette[relative_addr] = data; + } +} + byte ppu_read(address addr) { assert(addr < PPU_VRAM_SIZE); @@ -260,9 +350,10 @@ byte ppu_read(address addr) { relative_addr = addr - 0x2c00; return ppu_state.memory.nametable_1[relative_addr]; } else if (addr >= 0x3f00) { - relative_addr = (addr - 0x3f00) % 0x20; + relative_addr = (addr - 0x3f00) % PALETTE_TABLE_SIZE; return ppu_state.memory.palette[relative_addr]; } - assert(false); +// assert(false); + return 0; } \ No newline at end of file