浏览代码

Add PPU register R/W

v2
Nathaniel Walizer 9 个月前
父节点
当前提交
c44a0a8b74
共有 4 个文件被更改,包括 165 次插入14 次删除
  1. +97
    -5
      src/f6502.c
  2. +19
    -7
      src/nes.c
  3. +1
    -0
      src/port.h
  4. +48
    -2
      src/ppu.h

+ 97
- 5
src/f6502.c 查看文件

@@ -42,17 +42,40 @@ static inline uint8_t f6502_read(nes_Memory* mem,
return mem->rom_bank[(addr - 0x8000U) >> 13][addr & 0x1FFFFU];
}

switch (addr & 0x6000) {
switch (addr & 0x6000U) {
case 0x0000:
return mem->ram[addr & 0x7FFU];

case 0x2000:
// TODO: PPU Reg
return 0;
switch(addr & 0x7) {
case ppu_reg_status:
{ uint8_t val = mem->ppu.status;

mem->ppu.status &= ~ppu_Status_VBlank;

mem->ppu.latch = 0;

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

return val;
}

case ppu_reg_oam_data:
return mem->ppu.oam[mem->ppu.oam_addr++];

case ppu_reg_data:
{ addr = mem->ppu.addr & 0x3FFFU;
mem->ppu.addr += mem->ppu.addr_inc;
uint8_t val = mem->ppu.data;
mem->ppu.data = mem->ppu.bank[addr >> 10][addr & 0x3FFU];
return val;
}
}
break;

case 0x4000:
// TODO: APU Reg
return 0;
break;

case 0x6000:
if (mem->sram_bank) {
@@ -108,7 +131,76 @@ static inline bool f6502_write(nes_Memory* mem,
break;

case 0x2000:
// TODO: PPU
switch (addr & 0x7U) {
case ppu_reg_ctrl:
mem->ppu.ctrl &= ppu_Control_Nametable_Mask;
mem->ppu.ctrl |= (val & ~ppu_Control_Nametable_Mask);
mem->ppu.t &= ~(ppu_Control_Nametable_Mask << 10);
mem->ppu.t |= ((uint16_t)val & ppu_Control_Nametable_Mask) << 10;
mem->ppu.addr_inc = (val & ppu_Control_VRAM_Inc) ? 32 : 1;
// TODO: Trigger NMI if VBlank status is set?
break;

case ppu_reg_mask:
mem->ppu.mask = val;
break;

case ppu_reg_oam_addr:
mem->ppu.oam_addr = val;
break;

case ppu_reg_oam_data:
mem->ppu.oam[(int)mem->ppu.oam_addr++] = val;
break;

case ppu_reg_scroll:
if (mem->ppu.latch) {
mem->ppu.t &= 0b0000110000011111U;
mem->ppu.t |= (uint16_t)(val & 0b00000111U) << 12;
mem->ppu.t |= (uint16_t)(val & 0b11111000U) << 2;
} else {
mem->ppu.t &= ~(0b11111U);
mem->ppu.t |= (val & 0b11111000U) >> 3;
mem->ppu.x = (val & 0b111U);
}
mem->ppu.latch = !mem->ppu.latch;
break;

case ppu_reg_addr:
if (mem->ppu.latch) {
mem->ppu.t &= 0xFF00U;
mem->ppu.t |= val;
mem->ppu.addr = (mem->ppu.t & 0x3FFFU);
// Keep ctrl & t nametable in sync
mem->ppu.ctrl &= ~ppu_Control_Nametable_Mask;
mem->ppu.ctrl |= ((val >> 10) & ppu_Control_Nametable_Mask);
} else {
mem->ppu.t &= 0x00FFU;
mem->ppu.t |= (uint16_t)(val & 0x3FU) << 8;
}
mem->ppu.latch = !mem->ppu.latch;
break;

case ppu_reg_data:
// Disallow CHR ROM writes
if (addr >= NES_PPU_CHR_SIZE || mem->ppu.chr_ram) {
addr = mem->ppu.addr;
mem->ppu.bank[addr >> 10][addr & 0x3FFU] = val;
if (addr >= NES_PPU_PAL_START) {
addr = 0x300U | (addr & 0x1FU);
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;
}
}
}
}
mem->ppu.addr += mem->ppu.addr_inc;
break;
}
break;

case 0x4000:


+ 19
- 7
src/nes.c 查看文件

@@ -39,6 +39,16 @@ static int nes_hsync(nes* sys) {
// TODO: PPU draw line if visible
// TODO: PPU update regs

// TODO: This belongs in nes_ppu_drawline()
/*
if ( sys->ppu.scanline >= nes_ppu_visible_line &&
sys->ppu.scanline < nes_ppu_postrender_line) {
// TODO: Pass scanline buffer
nese_line_ready(NULL, sys->ppu.scanline -
nes_ppu_visible_line);
}
*/

sys->ppu.scanline++;
if (nes_ppu_frame_end_line == sys->ppu.scanline) {
sys->ppu.scanline = 0;
@@ -71,14 +81,16 @@ int nes_loop(nes* sys) {
int dots_remaining = nes_ppu_scanline_dots - dot;
int cpu_cycles = (dots_remaining + 2) / 3;
dot += 3 * f6502_step(&sys->core, cpu_cycles);
dot -= nes_ppu_scanline_dots;
// TODO: Validate dot >= 0?
nes_Memory* mem = &sys->core.memory;
if (NULL != mem->mapper.hsync) {
mem->mapper.hsync(&mem->mapper);
}

status = nes_hsync(sys);
// It's possible we returned due to a pending interrupt.
if (dot >= nes_ppu_scanline_dots) {
dot -= nes_ppu_scanline_dots;
nes_Memory* mem = &sys->core.memory;
if (NULL != mem->mapper.hsync) {
mem->mapper.hsync(&mem->mapper);
}
status = nes_hsync(sys);
}
}

return status;


+ 1
- 0
src/port.h 查看文件

@@ -9,6 +9,7 @@
void* nese_map_file(FILE* file, int size);
int nese_unmap_file(void* addr, int size);

int nese_line_ready(uint8_t* buffer, int line);
int nese_frame_ready();
int nese_update_input(nes_Input*);



+ 48
- 2
src/ppu.h 查看文件

@@ -11,6 +11,38 @@
#define nes_ppu_vblank_line (242U)
#define nes_ppu_frame_end_line (262U)


typedef enum {
ppu_reg_ctrl = 0,
ppu_reg_mask,
ppu_reg_status,
ppu_reg_oam_addr,
ppu_reg_oam_data,
ppu_reg_scroll,
ppu_reg_addr,
ppu_reg_data,
} ppu_reg_offset;

typedef enum {
ppu_Control_Nametable_Mask = 0b00000011,
ppu_Control_Scroll_Page_X = 0b00000001,
ppu_Control_Scroll_Page_Y = 0b00000010,
ppu_Control_VRAM_Inc = 0b00000100,
ppu_Control_Sprite_Bank = 0b00001000,
ppu_Control_Back_Bank = 0b00010000,
ppu_Control_Sprite_Size = 0b00100000,
ppu_Control_Master = 0b01000000,
ppu_Control_VBlank = 0b10000000,
} nes_ppu_Control;

typedef enum {
ppu_Status_Open_Bus_Mask = 0b00011111,
ppu_Status_Overflow = 0b00100000,
ppu_Status_Hit = 0b01000000,
ppu_Status_VBlank = 0b10000000,
} nes_ppu_Status;


#define NES_CHR_ROM_PAGE_SIZE (0x0400U)
#define NES_VRAM_PAGE_SIZE (0x0400U)

@@ -18,11 +50,25 @@
#define NES_PPU_VRAM_SIZE (0x1000U)
#define NES_PPU_RAM_SIZE (0x4000U)
#define NES_PPU_OAM_SIZE (64U * 4U)
#define NES_PPU_PAL_START (0x3F00U)

typedef struct __attribute__ ((__packed__)) {
typedef struct {
// Registers &c.
uint8_t ctrl;
uint8_t mask;
uint8_t status;
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)

// Memory banks
uint8_t* chr;
int n_chr_banks;
uint8_t* chr_ram; // Could this be a flag?
uint8_t* chr_ram; // TODO: Could this be a flag?
uint8_t* bank[16];
uint8_t vram[NES_PPU_VRAM_SIZE];
uint8_t pal_bank[NES_CHR_ROM_PAGE_SIZE]; // Mirrored in banks 12-15, also pal


正在加载...
取消
保存