Sfoglia il codice sorgente

Add PPU background rendering; bugfixes

- Fix palette reference
 - Fix live tracing memory addressing
 - Fix ROL bug
 - Fix indirect X/Y addressing decode
 - Fix nametable mirroring
v2
parent
commit
3b30f39a5b
7 ha cambiato i file con 147 aggiunte e 28 eliminazioni
  1. +21
    -6
      src/f6502.c
  2. +4
    -6
      src/f6502_opcodes.c
  3. +1
    -1
      src/f6502_opcodes.h
  4. +40
    -4
      src/linux/port.c
  5. +2
    -2
      src/nes.c
  6. +77
    -8
      src/ppu.c
  7. +2
    -1
      src/ppu.h

+ 21
- 6
src/f6502.c Vedi File

@@ -15,7 +15,7 @@
(type *)((char *)__mptr - offsetof(type,member));})
#endif

#define DEBUG "Core"
//#define DEBUG "Core"
#include "log.h"


@@ -158,10 +158,12 @@ static inline bool f6502_write(nes_Memory* mem,

case ppu_reg_scroll:
if (mem->ppu.latch) {
LOGI("PPU: SCROLL_Y %02X", val);
mem->ppu.t &= 0b0000110000011111U;
mem->ppu.t |= (uint16_t)(val & 0b00000111U) << 12;
mem->ppu.t |= (uint16_t)(val & 0b11111000U) << 2;
} else {
LOGI("PPU: SCROLL_X %02X", val);
mem->ppu.t &= ~(0b11111U);
mem->ppu.t |= (val & 0b11111000U) >> 3;
mem->ppu.x = (val & 0b111U);
@@ -192,7 +194,18 @@ static inline bool f6502_write(nes_Memory* mem,
LOGI("PPU W %04X < %02X", addr, val);
mem->ppu.bank[addr >> 10][addr & 0x3FFU] = val;
if (addr >= NES_PPU_PAL_START) {
addr = 0x300U | (addr & 0x1FU);
// Copy to render reference
addr &= 0x1FU;
mem->ppu.palette[addr] = val;
if (0 == (addr & 0xF)) {
uint8_t* pal = mem->ppu.palette;
pal[0] = pal[4] = pal[8] = pal[12] =
pal[16] = pal[20] = pal[24] =
pal[28] = val;
}

// Memory-mapped mirroring
addr |= 0x300U;
for (int mirror = 0; mirror < 0x100; mirror += 0x20) {
mem->ppu.pal_bank[addr + mirror] = val;
}
@@ -442,7 +455,7 @@ static inline int f6502_check_interrupts(f6502_Core* core) {
#define ROL(a) ({ \
register uint16_t addr = a; \
register uint8_t val = f6502_read(&core->memory, addr); \
register uint8_t new_c = (A & 0x80) ? f6502_Status_C : 0; \
register uint8_t new_c = (val & 0x80) ? f6502_Status_C : 0; \
val = (val << 1) | (P & f6502_Status_C); \
CLR(P, f6502_Status_C | f6502_Status_Z | f6502_Status_N); \
P |= new_c; \
@@ -517,12 +530,14 @@ static int f6502_do_step(f6502_Core* core, int clocks) {
printf(" %02x", (int)f6502_read(&core->memory, i + f6502_Base_Stack));
putc('\n', stdout);

printf("INT %02x\n", core->interrupts);
// printf("INT %02x\n", core->interrupts);
uint16_t operand = f6502_read16(&core->memory, PC);

printf("$%04x: ", PC - 1);
f6502_dump_instr(core, PC - 1);
f6502_dump_instr(core, opcode, (uint8_t*)&operand);
printf(" -> ");
f6502_fprintf_instr(stdout, opcode, core->memory.ram + PC);

f6502_fprintf_instr(stdout, opcode, (uint8_t*)&operand);
putc('\n', stdout);
#endif
switch (opcode) {


+ 4
- 6
src/f6502_opcodes.c Vedi File

@@ -56,7 +56,7 @@ static int adr_zp_y(const uint8_t* operand, char* str, int max) {

static inline int adr_ind_reg(char reg, const uint8_t* op,
char* str, int max) {
return snprintf(str, max, "($%04X,%c)", *(uint16_t*)op, reg);
return snprintf(str, max, "($%02X,%c)", *(uint8_t*)op, reg);
}

static int adr_ind_x(const uint8_t* operand, char* str, int max) {
@@ -409,14 +409,12 @@ static int f6502_instr_size(uint8_t opcode) {
}
}

void f6502_dump_instr(f6502_Core* core, uint16_t addr) {
uint8_t opcode = core->memory.ram[addr];
void f6502_dump_instr(f6502_Core* core, uint8_t opcode, uint8_t* operand) {
int size = f6502_instr_size(opcode);
printf("$%02x", opcode);
if (size == 2) {
printf(" $%02x", core->memory.ram[addr + 1]);
printf(" $%02x", *(uint8_t*)operand);
} else if (size == 3) {
printf(" $%04x", core->memory.ram[addr + 1] |
((uint16_t)core->memory.ram[addr + 2] << 8));
printf(" $%04x", *(uint16_t*)operand);
}
}

+ 1
- 1
src/f6502_opcodes.h Vedi File

@@ -7,7 +7,7 @@
int f6502_fprintf_instr(FILE* file,
uint8_t opcode, uint8_t* operand);

void f6502_dump_instr(f6502_Core* core, uint16_t addr);
void f6502_dump_instr(f6502_Core* core, uint8_t opcode, uint8_t* operand);


#endif // F6502_OPCODES_H_

+ 40
- 4
src/linux/port.c Vedi File

@@ -34,6 +34,7 @@ typedef struct {
SDL_Renderer* renderer;
SDL_Texture* texture;
SDL_Surface* target;
SDL_Surface* scanline;
} platform_data;

// TODO: Return an action enum?
@@ -76,8 +77,17 @@ static SDL_Color nes_palette[64] = {
};

int nese_line_ready(void* plat_data, uint8_t* buffer, int line) {
// TODO: Render line to target?
(void)nes_palette;
platform_data* plat = (platform_data*)plat_data;

SDL_Rect rect = {
.x = 0,
.y = line,
.w = nes_ppu_render_w,
.h = 1,
};

SDL_BlitSurface(plat->scanline, NULL, plat->target, &rect);

return 0;
}

@@ -102,13 +112,22 @@ int nese_frame_ready(void* plat_data) {
t_last = t_now;
*/

// TODO: Render frame
// TODO: Time sync
SDL_UnlockTexture(plat->texture);
SDL_RenderCopy(plat->renderer, plat->texture,
NULL, NULL);
SDL_LockTextureToSurface(plat->texture, NULL,
&plat->target);

// TODO: Overlay

SDL_RenderPresent(plat->renderer);

status = process_events(plat->sys);

// TODO: Perform menu actions

// TODO: Time sync

return status;
}

@@ -166,6 +185,23 @@ static int plat_init(platform_data* plat) {
}
}

if (0 == status) {
plat->scanline = SDL_CreateRGBSurfaceWithFormatFrom(
plat->sys->ppu.line_data, nes_ppu_render_w, 1,
8, nes_ppu_render_w,
SDL_PIXELFORMAT_INDEX8
);
if (NULL == plat->scanline) {
LOGE("SDL: Failed to create scanline");
status = -1;
} else {
SDL_SetPaletteColors(
plat->scanline->format->palette,
nes_palette, 0U, 64U
);
}
}

return status;
}



+ 2
- 2
src/nes.c Vedi File

@@ -3,7 +3,7 @@
#include "nes.h"
#include "port.h"

#define DEBUG "NES"
//#define DEBUG "NES"
#include "log.h"


@@ -28,7 +28,7 @@ static int nes_vsync(nes* sys, void* plat) {

sys->core.memory.ppu.status |= ppu_Status_VBlank;
if (sys->core.memory.ppu.ctrl & ppu_Control_VBlank) {
LOGI("VBlank NMI");
LOGD("VBlank NMI");
f6502_set_NMI(&sys->core, 1);
}



+ 77
- 8
src/ppu.c Vedi File

@@ -2,7 +2,7 @@

#include "ppu.h"

#define DEBUG "PPU"
//#define DEBUG "PPU"
#include "log.h"


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

@@ -63,8 +63,7 @@ void nes_ppu_render_line(nes_PPU* ppu, nes_PPU_Memory* mem) {
if (!(mem->mask & ppu_Mask_Back)) {
memset(ptr, 0, nes_ppu_render_w);
} else {
/*
int bank = nes_PPU_Nametable_Bank_Index +
int back_bank = nes_PPU_Nametable_Bank_Index +
((mem->addr >> 10) & 3);

int y_coarse = (mem->addr >> 5) & 0x1F;
@@ -73,14 +72,84 @@ void nes_ppu_render_line(nes_PPU* ppu, nes_PPU_Memory* mem) {

const int bank_off = !!(mem->ctrl & ppu_Control_Back_Bank) << 2;

const uint8_t* nametable = mem->bank[bank] + (y_coarse * 32) + x_coarse;
const uint8_t* attrs = mem->bank[bank] + 0x3C0U + ((y_coarse >> 2) << 3);
const uint8_t* nametable = mem->bank[back_bank] + (y_coarse * 32) + x_coarse;
const uint8_t* attrs = mem->bank[back_bank] + 0x3C0U + ((y_coarse >> 2) << 3);

ptr += 8 - mem->x;
*/

// TODO: Omit if left column is masked?
{
const uint8_t* pal = mem->palette +
(((attrs[x_coarse >> 2] >>
( (x_coarse & 2) +
((y_coarse & 2) << 1))) & 3) << 2);
const int ch = *nametable;
const int bank = (ch >> 6) + bank_off;
const int addr_off = ((ch & 0x3F) << 4) + y_fine;
const uint8_t* data = mem->bank[bank] + addr_off;
const uint8_t pl0 = data[0];
const uint8_t pl1 = data[8];
const int pat0 = (pl0 & 0x55) | ((pl1 << 1) & 0xAA);
const int pat1 = ((pl0 >> 1) & 0x55) | (pl1 & 0xAA);
switch (mem->x) {
case 0: ptr[-8] = pal[(pat1 >> 6) & 3];
case 1: ptr[-7] = pal[(pat0 >> 6) & 3];
case 2: ptr[-6] = pal[(pat1 >> 4) & 3];
case 3: ptr[-5] = pal[(pat0 >> 4) & 3];
case 4: ptr[-4] = pal[(pat1 >> 2) & 3];
case 5: ptr[-3] = pal[(pat0 >> 2) & 3];
case 6: ptr[-2] = pal[(pat1 >> 0) & 3];
case 7: ptr[-1] = pal[(pat0 >> 0) & 3];
}
}

// TODO: Mapper ppu_bus

++x_coarse;
++nametable;

void __attribute__((always_inline)) inline render_bg(void) {
const uint8_t* pal = mem->palette +
(((attrs[x_coarse >> 2] >>
( (x_coarse & 2) +
((y_coarse & 2) << 1))) & 3) << 2);
const int ch = *nametable;
const int bank = (ch >> 6) + bank_off;
const int addr_off = ((ch & 0x3F) << 4) + y_fine;
const uint8_t* data = mem->bank[bank] + addr_off;
const uint8_t pl0 = data[0];
const uint8_t pl1 = data[8];
//const int pat0 = ((pl0 & 0x55) << 1) | ((pl1 & 0x55) << 2);
//const int pat1 = ((pl0 & 0xAA) << 0) | ((pl1 & 0xAA) << 1);
const int pat0 = (pl0 & 0x55) | ((pl1 << 1) & 0xAA);
const int pat1 = ((pl0 >> 1) & 0x55) | (pl1 & 0xAA);

ptr[0] = pal[(pat1 >> 6) & 3];
ptr[1] = pal[(pat0 >> 6) & 3];
ptr[2] = pal[(pat1 >> 4) & 3];
ptr[3] = pal[(pat0 >> 4) & 3];
ptr[4] = pal[(pat1 >> 2) & 3];
ptr[5] = pal[(pat0 >> 2) & 3];
ptr[6] = pal[(pat1 >> 0) & 3];
ptr[7] = pal[(pat0 >> 0) & 3];
ptr += 8;
}

// TODO: Draw Background
for (; x_coarse < 32; ++x_coarse) {
render_bg();
// TODO: Mapper ppu_bus
++nametable;
}

back_bank ^= 0b01;
nametable = mem->bank[back_bank] + (y_coarse * 32);
attrs = mem->bank[back_bank] + 0x3C0U + ((y_coarse >> 2) << 3);

for (x_coarse = 0; x_coarse < (mem->addr & 0x1FU); ++x_coarse) {
render_bg();
// TODO: Mapper ppu_bus
++nametable;
}
}

// TODO: Draw Sprites


+ 2
- 1
src/ppu.h Vedi File

@@ -98,8 +98,9 @@ typedef struct {
int n_chr_banks;
uint8_t* chr_ram; // TODO: Could this be a flag?
uint8_t* bank[16];
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]; // Mirrored in banks 12-15, also pal
uint8_t pal_bank[NES_CHR_ROM_PAGE_SIZE]; // Raw palette data mirrored in banks 12-15, also pal
uint8_t oam[NES_PPU_OAM_SIZE];
} nes_PPU_Memory;



Loading…
Annulla
Salva