#include #include "nes.h" #include "port.h" //#define NESE_DEBUG "NES" #include "log.h" void nes_init(nes* sys) { f6502_init(&sys->core); // TODO: Init PPU // TODO: Init APU } void nes_reset(nes* sys) { f6502_reset(&sys->core); // TODO: Reset PPU // TODO: Reset APU } void nes_done(nes* sys) { // TODO: deallocate RAM, etc. } static int nes_vsync(nes* sys, void* plat) { int status = 0; sys->core.memory.ppu.status |= ppu_Status_VBlank; if (sys->core.memory.ppu.ctrl & ppu_Control_VBlank) { LOGD("VBlank NMI"); f6502_set_NMI(&sys->core, 1); } // TODO: APU Sync // TODO: APU Frame IRQ nes_Memory* mem = &sys->core.memory; if (0 == status && NULL != mem->mapper.vsync) { mem->mapper.vsync(&mem->mapper); status = nese_update_input(plat, &mem->input); } return status; } static int nes_hsync(nes* sys, void* plat) { int status = 0; // TODO: APU sync if (sys->ppu.scanline < nes_ppu_postrender_line) { if (sys->ppu.scanline < nes_ppu_visible_line) { if ( sys->core.memory.ppu.mask & (ppu_Mask_Sprite | ppu_Mask_Back)) { sys->core.memory.ppu.addr = sys->core.memory.ppu.t; } } else { nes_ppu_render_line(&sys->ppu, &sys->core.memory.ppu); nese_line_ready( plat, sys->ppu.screen_data + ( nes_ppu_render_w * ( sys->ppu.scanline - nes_ppu_visible_line)), sys->ppu.scanline - nes_ppu_visible_line ); } } sys->ppu.scanline++; if (nes_ppu_frame_end_line == sys->ppu.scanline) { sys->ppu.scanline = nes_ppu_prerender_line; } switch (sys->ppu.scanline) { case nes_ppu_prerender_line: { nes_PPU_Memory* mem = &sys->core.memory.ppu; f6502_set_NMI(&sys->core, 0); mem->status &= ~(ppu_Status_VBlank | ppu_Status_Hit); nes_ppu_find_hit_line(&sys->ppu, mem); // Emulate the happy part of the backdrop override quirk int pal_idx = ((mem->addr & 0x3F00U) == 0x3F00U) ? (mem->addr & 0x1FU) : 0; LOGD("Background: %d", pal_idx); // Don't use the rendering palette (masked transparency) status = nese_frame_start(plat, mem->pal_bank[0x300 + pal_idx] & 0x3FU); } break; case nes_ppu_postrender_line: status = nese_frame_ready(plat); break; case nes_ppu_vblank_line: status = nes_vsync(sys, plat); break; } return status; } #define mapper_hsync_dot (300U) int nes_loop(nes* sys, void* plat) { int status = 0; int dot = 0; bool mapper_hsync = false; while (0 == status) { // TODO: Move to inline PPU function? int target_dot = nes_ppu_scanline_dots; if ( sys->core.memory.mapper.hsync && sys->ppu.scanline > nes_ppu_prerender_line && sys->ppu.scanline < nes_ppu_postrender_line && dot < mapper_hsync_dot) { target_dot = mapper_hsync_dot; mapper_hsync = true; } if ( sys->ppu.hit_line == sys->ppu.scanline && ( sys->core.memory.ppu.mask & (ppu_Mask_Sprite | ppu_Mask_Back)) && !(sys->core.memory.ppu.status & ppu_Status_Hit)) { int hit_dot = ((oam_sprite*)sys->core.memory.ppu.oam)->x; if (dot >= hit_dot) { LOGD("Line %d sprite 0 hit @ %d", sys->ppu.hit_line, hit_dot); sys->core.memory.ppu.status |= ppu_Status_Hit; } else { target_dot = hit_dot; } } if (target_dot > dot) { int cpu_cycles = (target_dot - dot + 2) / 3; dot += 3 * f6502_step(&sys->core, cpu_cycles); } if (mapper_hsync && dot >= mapper_hsync_dot) { nes_Memory* mem = &sys->core.memory; mem->mapper.hsync(&mem->mapper); mapper_hsync = false; } // It's possible we returned due to a pending interrupt. if (dot >= nes_ppu_scanline_dots) { dot -= nes_ppu_scanline_dots; status = nes_hsync(sys, plat); } } return status; }