浏览代码

Various NMI and reading PPU fixes

- NMI still imperfect
 - Passes vast majority of bisqwit's ppu_read_buffer tests
v2
Nathaniel Walizer 8 个月前
父节点
当前提交
8e13f8c30b
共有 6 个文件被更改,包括 94 次插入63 次删除
  1. +1
    -6
      src/cart.c
  2. +48
    -36
      src/f6502.c
  3. +5
    -1
      src/linux/port.c
  4. +27
    -10
      src/nes.c
  5. +2
    -1
      src/ppu.c
  6. +11
    -9
      src/ppu.h

+ 1
- 6
src/cart.c 查看文件

@@ -102,16 +102,11 @@ int nes_cart_load_mem(void* data, int size, nes* sys) {
mem->ppu.bank[page] = chr_page(&mem->ppu, page); mem->ppu.bank[page] = chr_page(&mem->ppu, page);
} }


// PPU Bank 8 - 11: Mirrored VRAM
// PPU Bank 8 - 11 (+ 12 - 15): Mirrored VRAM
nes_ppu_set_mirroring( nes_ppu_set_mirroring(
&mem->ppu, hdr->flags_6 & ines_Flag_Vert_Mirror &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;
}

if (mem->mapper.init) { if (mem->mapper.init) {
status = mem->mapper.init(&mem->mapper, status = mem->mapper.init(&mem->mapper,
hdr, mem); hdr, mem);


+ 48
- 36
src/f6502.c 查看文件

@@ -69,28 +69,27 @@ static inline uint8_t f6502_read(nes_Memory* mem,


// TODO: Set nametable to 0 when in VBlank?? // TODO: Set nametable to 0 when in VBlank??


return val;
}
mem->ppu.bus = (val & ~ppu_Status_Open_Bus_Mask) |
(mem->ppu.bus & ppu_Status_Open_Bus_Mask);
} break;


case ppu_reg_oam_data: case ppu_reg_oam_data:
return mem->ppu.oam[mem->ppu.oam_addr];
mem->ppu.bus = mem->ppu.oam[mem->ppu.oam_addr];
break;


case ppu_reg_data: case ppu_reg_data:
{ addr = mem->ppu.addr & 0x3FFFU;
addr = mem->ppu.addr & 0x3FFFU;
mem->ppu.addr += mem->ppu.addr_inc; mem->ppu.addr += mem->ppu.addr_inc;
uint8_t data = mem->ppu.bank[addr >> 10][addr & 0x3FFU];
if (addr >= 0x3F00) {
if (addr >= NES_PPU_PAL_START) {
mem->ppu.bus = mem->ppu.pal_ram[addr & 0x1FU];
addr &= 0x2FFFU; addr &= 0x2FFFU;
mem->ppu.data = mem->ppu.bank[addr >> 10][addr & 0x3FFU];
return data;
} else {
uint8_t val = mem->ppu.data;
mem->ppu.data = data;
return val;
} else {
mem->ppu.bus = mem->ppu.data;
} }
mem->ppu.data = mem->ppu.bank[addr >> 10][addr & 0x3FFU];
break;
} }
}
break;
return mem->ppu.bus;


case 0x4000: case 0x4000:
switch (addr & 0x1FU) { switch (addr & 0x1FU) {
@@ -136,6 +135,17 @@ static inline uint16_t f6502_read16(nes_Memory* mem,
((uint16_t)f6502_read(mem, addr + 1) << 8)); ((uint16_t)f6502_read(mem, addr + 1) << 8));
} }



typedef struct {
uint16_t clocks;
uint16_t interrupt;
} write_ret;

#define INT_TRIGGERED(x) (x >> 16)
#define CLOCKS(x) ((uint16_t)x)

#define TRIGGER_INT (1U << 16)

static inline int f6502_write(nes_Memory* mem, static inline int f6502_write(nes_Memory* mem,
uint16_t addr, uint8_t val) { uint16_t addr, uint8_t val) {
int ret = 0; int ret = 0;
@@ -163,9 +173,7 @@ static inline int f6502_write(nes_Memory* mem,
f6502_set_IRQ(core, irq); f6502_set_IRQ(core, irq);


// Interrupt [may have been] triggered // Interrupt [may have been] triggered
// We're not strictly counting cycles,
// so just overload the counter
ret = 1000;
ret = TRIGGER_INT;
} }
#endif #endif
mem->ram[addr] = val; mem->ram[addr] = val;
@@ -178,6 +186,14 @@ static inline int f6502_write(nes_Memory* mem,
case 0x2000: case 0x2000:
switch (addr & 0x7U) { switch (addr & 0x7U) {
case ppu_reg_ctrl: case ppu_reg_ctrl:
if ( (mem->ppu.status & ppu_Status_VBlank) &&
!(mem->ppu.ctrl & ppu_Control_VBlank) &&
(val & ppu_Control_VBlank)) {
f6502_Core* core = container_of(mem, f6502_Core, memory);
/* Trigger NMI if VBlank was set */
f6502_set_NMI(core, 1);
ret = TRIGGER_INT;
}
mem->ppu.ctrl &= ppu_Control_Nametable_Mask; mem->ppu.ctrl &= ppu_Control_Nametable_Mask;
mem->ppu.ctrl |= (val & ~ppu_Control_Nametable_Mask); mem->ppu.ctrl |= (val & ~ppu_Control_Nametable_Mask);
mem->ppu.t &= ~(ppu_Control_Nametable_Mask << 10); mem->ppu.t &= ~(ppu_Control_Nametable_Mask << 10);
@@ -233,25 +249,19 @@ static inline int f6502_write(nes_Memory* mem,
addr = mem->ppu.addr & 0x3FFFU; addr = mem->ppu.addr & 0x3FFFU;
if (addr >= NES_PPU_CHR_SIZE || mem->ppu.chr_ram) { if (addr >= NES_PPU_CHR_SIZE || mem->ppu.chr_ram) {
LOGI("PPU W %04X < %02X", addr, val); LOGI("PPU W %04X < %02X", addr, val);
mem->ppu.bank[addr >> 10][addr & 0x3FFU] = val;
if (addr >= NES_PPU_PAL_START) { if (addr >= NES_PPU_PAL_START) {
// Copy to render reference
// Copy to palette RAM
addr &= 0x1FU; addr &= 0x1FU;
uint8_t* pal = mem->ppu.palette;
mem->ppu.pal_ram[addr] = val;
if (0 != (addr & 0x3)) { if (0 != (addr & 0x3)) {
pal[addr] = val & 0x3FU;
}

// Memory-mapped mirroring
addr |= 0x300U;
for (int mirror = 0; mirror < 0x100; mirror += 0x20) {
mem->ppu.pal_bank[addr + mirror] = val;
}
if (0 == (addr & 3)) {
for (int mirror = 0; mirror < 0x100; mirror += 0x20) {
mem->ppu.pal_bank[(addr ^ 0x10U) + mirror] = val;
}
// Copy to render reference
mem->ppu.palette[addr] = val & 0x3FU;
} else {
// Mirror
mem->ppu.pal_ram[addr ^ 0x10] = val;
} }
} else {
mem->ppu.bank[addr >> 10][addr & 0x3FFU] = val;
} }
} else { } else {
LOGE("PPU W %04X < %02X : INVALID", addr, val); LOGE("PPU W %04X < %02X : INVALID", addr, val);
@@ -259,6 +269,7 @@ static inline int f6502_write(nes_Memory* mem,
mem->ppu.addr += mem->ppu.addr_inc; mem->ppu.addr += mem->ppu.addr_inc;
break; break;
} }
mem->ppu.bus = val;
break; break;


case 0x4000: case 0x4000:
@@ -533,7 +544,7 @@ static inline int f6502_check_interrupts(f6502_Core* core) {
#define BIF_HCF() \ #define BIF_HCF() \
if (pc_prev == PC + 2) { \ if (pc_prev == PC + 2) { \
++PC; \ ++PC; \
clocks_elapsed = -clocks_elapsed; \
clocks_elapsed += INT_TRIGGER; \
goto step_done; \ goto step_done; \
} }
#else #else
@@ -647,7 +658,8 @@ static inline int f6502_check_interrupts(f6502_Core* core) {
} \ } \
A = res; \ A = res; \
} }
#define ST(reg, a) clocks_elapsed += f6502_write(&core->memory, (a), reg)
#define ST(reg, a) \
clocks_elapsed += f6502_write(&core->memory, (a), reg)




static int f6502_do_step(f6502_Core* core, int clocks) { static int f6502_do_step(f6502_Core* core, int clocks) {
@@ -1712,8 +1724,8 @@ int f6502_step(f6502_Core* core, int clocks) {
} }
*/ */
clocks_elapsed += f6502_check_interrupts(core); clocks_elapsed += f6502_check_interrupts(core);
clocks_elapsed += f6502_do_step(core,
clocks - clocks_elapsed);
clocks = f6502_do_step(core, clocks - clocks_elapsed);
clocks_elapsed += CLOCKS(clocks);
core->clocks += clocks_elapsed; core->clocks += clocks_elapsed;
return clocks_elapsed; return clocks_elapsed;
} }


+ 5
- 1
src/linux/port.c 查看文件

@@ -15,9 +15,11 @@
#include "port.h" #include "port.h"
#include "save.h" #include "save.h"


#define DEBUG "Port"
#define NESE_DEBUG "Port"
#include "log.h" #include "log.h"


//#define NESE_TURBO



/* /*
* OS-specific file operations * OS-specific file operations
@@ -321,6 +323,7 @@ int nese_frame_ready(void* plat_data) {


// TODO: Perform more actions // TODO: Perform more actions


#ifndef NESE_TURBO
if (0 == status) { if (0 == status) {
plat->t_target += FRAME_TIME_NS; plat->t_target += FRAME_TIME_NS;
int64_t slept_ns = time_sleep_until(plat->t_target); int64_t slept_ns = time_sleep_until(plat->t_target);
@@ -330,6 +333,7 @@ int nese_frame_ready(void* plat_data) {
printf("Out of sync: %d\n", (int)slept_ns); printf("Out of sync: %d\n", (int)slept_ns);
} }
} }
#endif // NESE_TURBO


return status; return status;
} }


+ 27
- 10
src/nes.c 查看文件

@@ -32,12 +32,6 @@ void nes_done(nes* sys) {
static int nes_vsync(nes* sys, void* plat) { static int nes_vsync(nes* sys, void* plat) {
int status = 0; 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);
}

nes_Memory* mem = &sys->core.memory; nes_Memory* mem = &sys->core.memory;
if (0 == status && NULL != mem->mapper.vsync) { if (0 == status && NULL != mem->mapper.vsync) {
mem->mapper.vsync(&mem->mapper); mem->mapper.vsync(&mem->mapper);
@@ -81,6 +75,7 @@ static int nes_hsync(nes* sys, void* plat) {
sys->ppu.scanline = nes_ppu_prerender_line; sys->ppu.scanline = nes_ppu_prerender_line;
} }


// Beginning of the next line
switch (sys->ppu.scanline) { switch (sys->ppu.scanline) {
case nes_ppu_prerender_line: case nes_ppu_prerender_line:
{ nes_PPU_Memory* mem = &sys->core.memory.ppu; { nes_PPU_Memory* mem = &sys->core.memory.ppu;
@@ -94,7 +89,7 @@ static int nes_hsync(nes* sys, void* plat) {
int pal_idx = ((mem->addr & 0x3F00U) == 0x3F00U) ? int pal_idx = ((mem->addr & 0x3F00U) == 0x3F00U) ?
(mem->addr & 0x1FU) : 0; (mem->addr & 0x1FU) : 0;
// Don't use the rendering palette (masked transparency) // Don't use the rendering palette (masked transparency)
status = nese_frame_start(plat, mem->pal_bank[0x300 + pal_idx] & 0x3FU);
status = nese_frame_start(plat, mem->pal_ram[pal_idx] & 0x3FU);


} break; } break;


@@ -110,21 +105,23 @@ static int nes_hsync(nes* sys, void* plat) {
return status; return status;
} }


#define vblank_set_dot (1U)
#define mapper_hsync_dot (300U) #define mapper_hsync_dot (300U)
#define render_dot (256U) #define render_dot (256U)
#define vblank_clear_dot (320U)
#define vblank_clear_dot (341U)


int nes_loop(nes* sys, void* plat) { int nes_loop(nes* sys, void* plat) {
int status = 0; int status = 0;
int dot = 0; int dot = 0;
bool mapper_hsync = false; bool mapper_hsync = false;
bool vbl_clear = false; bool vbl_clear = false;
bool vbl_set = false;


while (0 == status) { while (0 == status) {
int target_dot = nes_ppu_scanline_dots; int target_dot = nes_ppu_scanline_dots;


if ( nes_ppu_frame_end_line - 1 == sys->ppu.scanline && if ( nes_ppu_frame_end_line - 1 == sys->ppu.scanline &&
sys->core.memory.ppu.status & ppu_Status_VBlank) {
(sys->core.memory.ppu.status & ppu_Status_VBlank)) {
target_dot = vblank_clear_dot; target_dot = vblank_clear_dot;
vbl_clear = true; vbl_clear = true;
} }
@@ -137,6 +134,13 @@ int nes_loop(nes* sys, void* plat) {
mapper_hsync = true; mapper_hsync = true;
} }


if ( vbl_set ||
( nes_ppu_vblank_line == sys->ppu.scanline &&
dot < vblank_set_dot )) {
target_dot = vblank_set_dot;
vbl_set = true;
}

if ( sys->ppu.hit_line == sys->ppu.scanline && if ( sys->ppu.hit_line == sys->ppu.scanline &&
( sys->core.memory.ppu.mask & ( sys->core.memory.ppu.mask &
(ppu_Mask_Sprite | ppu_Mask_Back)) && (ppu_Mask_Sprite | ppu_Mask_Back)) &&
@@ -145,7 +149,7 @@ int nes_loop(nes* sys, void* plat) {
if (dot >= hit_dot) { if (dot >= hit_dot) {
LOGD("Line %d sprite 0 hit @ %d", sys->ppu.hit_line, hit_dot); LOGD("Line %d sprite 0 hit @ %d", sys->ppu.hit_line, hit_dot);
sys->core.memory.ppu.status |= ppu_Status_Hit; sys->core.memory.ppu.status |= ppu_Status_Hit;
} else {
} else if (target_dot > hit_dot) {
target_dot = hit_dot; target_dot = hit_dot;
} }
} }
@@ -176,6 +180,15 @@ int nes_loop(nes* sys, void* plat) {
dot += 3 * f6502_step(&sys->core, cpu_cycles); dot += 3 * f6502_step(&sys->core, cpu_cycles);
} }


if (vbl_set && dot >= vblank_set_dot) {
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);
}
vbl_set = false;
}

if (vbl_clear && dot >= vblank_clear_dot) { if (vbl_clear && dot >= vblank_clear_dot) {
sys->core.memory.ppu.status &= ~ppu_Status_VBlank; sys->core.memory.ppu.status &= ~ppu_Status_VBlank;
vbl_clear = false; vbl_clear = false;
@@ -191,6 +204,10 @@ int nes_loop(nes* sys, void* plat) {
if (dot >= nes_ppu_scanline_dots) { if (dot >= nes_ppu_scanline_dots) {
dot -= nes_ppu_scanline_dots; dot -= nes_ppu_scanline_dots;
status = nes_hsync(sys, plat); status = nes_hsync(sys, plat);
if ( nes_ppu_vblank_line == sys->ppu.scanline &&
dot >= vblank_set_dot ) {
vbl_set = true;
}
} }
} }




+ 2
- 1
src/ppu.c 查看文件

@@ -30,7 +30,8 @@ static const uint8_t mirror_schemes[][4] = {
void nes_ppu_set_mirroring(nes_PPU_Memory* mem, void nes_ppu_set_mirroring(nes_PPU_Memory* mem,
nes_Nametable_Mirroring mirror) { nes_Nametable_Mirroring mirror) {
for (int i = 0; i < 4; ++i) { for (int i = 0; i < 4; ++i) {
mem->bank[nes_PPU_Nametable_Bank_Index + i] =
mem->bank[nes_PPU_Nametable_Bank_Index + i + 4] =
mem->bank[nes_PPU_Nametable_Bank_Index + i] =
vram_page(mem, (int)mirror_schemes[mirror][i]); vram_page(mem, (int)mirror_schemes[mirror][i]);
} }
} }


+ 11
- 9
src/ppu.h 查看文件

@@ -97,19 +97,21 @@ typedef struct {
uint8_t mask; uint8_t mask;
uint8_t status; uint8_t status;
uint8_t oam_addr; uint8_t oam_addr;
uint16_t addr; // aka "v"
uint8_t data;
uint8_t x; // Fine X scroll
uint16_t t; // temp VRAM addr
uint8_t latch; // aka "w" - TODO: Could this be a flag?
uint8_t addr_inc; // Auto-increment (1 or 32)
uint16_t addr; // aka "v"
uint8_t data; // Next incremented data val (NOT last value)
uint8_t x; // Fine X scroll
uint16_t t; // temp VRAM addr
uint8_t latch; // aka "w" - TODO: Could this be a flag?
uint8_t addr_inc; // Auto-increment (1 or 32)
uint8_t unused[2]; // 2 B unused
uint8_t bus; // Last value transmitted (NOT next data val)


// Static memory banks // Static memory banks
int n_chr_banks;
uint8_t n_chr_banks; // TODO: Changed from int; consequences?
uint8_t palette[32]; // Rendering palette with transparency masked uint8_t palette[32]; // Rendering palette with transparency masked
uint8_t vram[NES_PPU_VRAM_SIZE];
uint8_t pal_bank[NES_CHR_ROM_PAGE_SIZE]; // Raw palette data mirrored in banks 12-15, also pal
uint8_t pal_ram[32]; // Raw palette data in RAM
uint8_t oam[NES_PPU_OAM_SIZE]; uint8_t oam[NES_PPU_OAM_SIZE];
uint8_t vram[NES_PPU_VRAM_SIZE];
} nes_PPU_Memory; } nes_PPU_Memory;






正在加载...
取消
保存