Browse Source

PPU rendering stub; interrupt and memory bugfixes

v2
Nathaniel Walizer 9 months ago
parent
commit
0370da8538
10 changed files with 235 additions and 34 deletions
  1. +2
    -0
      Makefile
  2. +8
    -1
      src/f6502.c
  3. +133
    -7
      src/linux/port.c
  4. +28
    -19
      src/nes.c
  5. +1
    -1
      src/nes.h
  6. +2
    -2
      src/nese.c
  7. +1
    -1
      src/nese.h
  8. +3
    -3
      src/port.h
  9. +54
    -0
      src/ppu.c
  10. +3
    -0
      src/ppu.h

+ 2
- 0
Makefile View File

@@ -45,6 +45,8 @@ all: $(BINDIR)/nese
$(BINDIR)/test: CFLAGS += -DF6502_FLAT -DF6502_TEST -DF6502_TRACE

$(BINDIR)/nese: CFLAGS += $(foreach debug,$(NESE_DEBUG), -DDEBUG_$(debug))
$(BINDIR)/nese: CFLAGS += $(shell sdl2-config --cflags)
$(BINDIR)/nese: LDFLAGS += $(shell sdl2-config --libs)
$(BINDIR)/nese: $(NESE_OBJS)
@mkdir -p $(@D)
$(LD) $^ $(LDFLAGS) -o $@


+ 8
- 1
src/f6502.c View File

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

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


// TOTO: Temp Hack
volatile uint8_t memval;
@@ -174,6 +177,7 @@ static inline bool f6502_write(nes_Memory* mem,
// Keep ctrl & t nametable in sync
mem->ppu.ctrl &= ~ppu_Control_Nametable_Mask;
mem->ppu.ctrl |= ((val >> 10) & ppu_Control_Nametable_Mask);
LOGI("PPU: ADDR %04X", mem->ppu.addr);
} else {
mem->ppu.t &= 0x00FFU;
mem->ppu.t |= (uint16_t)(val & 0x3FU) << 8;
@@ -183,8 +187,9 @@ static inline bool f6502_write(nes_Memory* mem,

case ppu_reg_data:
// Disallow CHR ROM writes
addr = mem->ppu.addr & 0x3FFFU;
if (addr >= NES_PPU_CHR_SIZE || mem->ppu.chr_ram) {
addr = mem->ppu.addr;
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);
@@ -197,6 +202,8 @@ static inline bool f6502_write(nes_Memory* mem,
}
}
}
} else {
LOGE("PPU W %04X < %02X : INVALID", addr, val);
}
mem->ppu.addr += mem->ppu.addr_inc;
break;


+ 133
- 7
src/linux/port.c View File

@@ -4,6 +4,8 @@
#include <time.h>
#include <sys/mman.h>

#include <SDL.h>

#include "nese.h"
#include "port.h"

@@ -25,7 +27,63 @@ int nese_unmap_file(void* addr, int size) {
return munmap(addr, size);
}

int nese_frame_ready() {

typedef struct {
nes* sys;
SDL_Window* window;
SDL_Renderer* renderer;
SDL_Texture* texture;
SDL_Surface* target;
} platform_data;

// TODO: Return an action enum?
static int process_events(nes* sys) {
int status = 0;

SDL_Event event = {0};
while (0 == status && 0 != SDL_PollEvent(&event)) {
if (SDL_QUIT == event.type) {
status = -1;
}

// TODO: Update gamepad
// TODO: Menu actions, &c.
}

return status;
}

static SDL_Color nes_palette[64] = {
{0x80,0x80,0x80}, {0x00,0x00,0xBB}, {0x37,0x00,0xBF}, {0x84,0x00,0xA6},
{0xBB,0x00,0x6A}, {0xB7,0x00,0x1E}, {0xB3,0x00,0x00}, {0x91,0x26,0x00},
{0x7B,0x2B,0x00}, {0x00,0x3E,0x00}, {0x00,0x48,0x0D}, {0x00,0x3C,0x22},
{0x00,0x2F,0x66}, {0x00,0x00,0x00}, {0x05,0x05,0x05}, {0x05,0x05,0x05},

{0xC8,0xC8,0xC8}, {0x00,0x59,0xFF}, {0x44,0x3C,0xFF}, {0xB7,0x33,0xCC},
{0xFF,0x33,0xAA}, {0xFF,0x37,0x5E}, {0xFF,0x37,0x1A}, {0xD5,0x4B,0x00},
{0xC4,0x62,0x00}, {0x3C,0x7B,0x00}, {0x1E,0x84,0x15}, {0x00,0x95,0x66},
{0x00,0x84,0xC4}, {0x11,0x11,0x11}, {0x09,0x09,0x09}, {0x09,0x09,0x09},

{0xFF,0xFF,0xFF}, {0x00,0x95,0xFF}, {0x6F,0x84,0xFF}, {0xD5,0x6F,0xFF},
{0xFF,0x77,0xCC}, {0xFF,0x6F,0x99}, {0xFF,0x7B,0x59}, {0xFF,0x91,0x5F},
{0xFF,0xA2,0x33}, {0xA6,0xBF,0x00}, {0x51,0xD9,0x6A}, {0x4D,0xD5,0xAE},
{0x00,0xD9,0xFF}, {0x66,0x66,0x66}, {0x0D,0x0D,0x0D}, {0x0D,0x0D,0x0D},

{0xFF,0xFF,0xFF}, {0x84,0xBF,0xFF}, {0xBB,0xBB,0xFF}, {0xD0,0xBB,0xFF},
{0xFF,0xBF,0xEA}, {0xFF,0xBF,0xCC}, {0xFF,0xC4,0xB7}, {0xFF,0xCC,0xAE},
{0xFF,0xD9,0xA2}, {0xCC,0xE1,0x99}, {0xAE,0xEE,0xB7}, {0xAA,0xF7,0xEE},
{0xB3,0xEE,0xFF}, {0xDD,0xDD,0xDD}, {0x11,0x11,0x11}, {0x11,0x11,0x11}
};

int nese_line_ready(void* plat_data, uint8_t* buffer, int line) {
// TODO: Render line to target?
(void)nes_palette;
return 0;
}

int nese_frame_ready(void* plat_data) {
int status = 0;
platform_data* plat = (platform_data*)plat_data;
/*
static int frame = 0;
static int ignore = 1;
@@ -46,27 +104,95 @@ int nese_frame_ready() {

// TODO: Render frame
// TODO: Time sync
// TODO: Handle quit signal

status = process_events(plat->sys);

// TODO: Perform menu actions
return 0;

return status;
}

int nese_update_input(nes_Input* input) {
int nese_update_input(void* plat_data, nes_Input* input) {
// TODO: Populate the gamepad states
return 0;
}

static int plat_init(platform_data* plat) {
int status = SDL_Init(
SDL_INIT_EVENTS |
SDL_INIT_VIDEO
);

if (0 == status) {
plat->window = SDL_CreateWindow(
"NESe",
SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED,
nes_ppu_render_w * 8,
nes_ppu_render_h * 7,
0
);
if (NULL == plat->window) {
LOGE("SDL: Failed to create window");
status = -1;
}
}

if (0 == status) {
plat->renderer = SDL_CreateRenderer(
plat->window, -1,
SDL_RENDERER_ACCELERATED |
SDL_RENDERER_PRESENTVSYNC
);
if (NULL == plat->renderer) {
LOGE("SDL: Failed to create renderer");
status = -1;
}
}

if (0 == status) {
plat->texture = SDL_CreateTexture(
plat->renderer, SDL_PIXELFORMAT_RGB888,
SDL_TEXTUREACCESS_STREAMING,
nes_ppu_render_w, nes_ppu_render_h
);
if (NULL == plat->texture) {
LOGE("SDL: Failed to create target");
status = -1;
} else {
SDL_LockTextureToSurface(plat->texture, NULL,
&plat->target);
// SDL_FillRect(data->target, NULL, color_background);
}
}

return status;
}

static void plat_done(platform_data* plat) {
if (NULL != plat->texture) SDL_DestroyTexture(plat->texture);
if (NULL != plat->renderer) SDL_DestroyRenderer(plat->renderer);
if (NULL != plat->window) SDL_DestroyWindow(plat->window);
SDL_Quit();
}


// This is too big for the stack.
static nes sys = {0};


int main(int argc, char* argv[]) {
int status = 0;
// This should be tiny enough to keep on the stack.
platform_data plat = {
.sys = &sys,
};
int status = plat_init(&plat);
if (argc <= 1) {
LOGE("No ROM file provided.");
status = -1;
} else {
status = nese_start(&sys, argv[1]);
} else if (0 == status) {
status = nese_start(&sys, argv[1], &plat);
}
plat_done(&plat);
return status;
}

+ 28
- 19
src/nes.c View File

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

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


void nes_init(nes* sys) {
f6502_init(&sys->core);
@@ -20,11 +23,12 @@ void nes_done(nes* sys) {
// TODO: deallocate RAM, etc.
}

static int nes_vsync(nes* sys) {
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) {
LOGI("VBlank NMI");
f6502_set_NMI(&sys->core, 1);
}

@@ -34,30 +38,33 @@ static int nes_vsync(nes* sys) {
nes_Memory* mem = &sys->core.memory;
if (0 == status && NULL != mem->mapper.vsync) {
mem->mapper.vsync(&mem->mapper);
status = nese_update_input(&sys->input);
status = nese_update_input(plat, &sys->input);
}

return status;
}

static int nes_hsync(nes* sys) {
static int nes_hsync(nes* sys, void* plat) {
int status = 0;

// TODO: APU sync

// TODO: PPU Update H Bytes & Select Nametable
// 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);
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.line_data,
sys->ppu.scanline - nes_ppu_visible_line
);
}
}
*/

sys->ppu.scanline++;
if (nes_ppu_frame_end_line == sys->ppu.scanline) {
@@ -73,22 +80,23 @@ static int nes_hsync(nes* sys) {
break;

case nes_ppu_postrender_line:
status = nese_frame_ready();
status = nese_frame_ready(plat);
break;

case nes_ppu_vblank_line:
status = nes_vsync(sys);
status = nes_vsync(sys, plat);
break;
}

return status;
}

int nes_loop(nes* sys) {
int nes_loop(nes* sys, void* plat) {
int status = 0;
int dot = 0;

while (0 == status) {
// TODO: Move to inline PPU function
int dots_remaining = nes_ppu_scanline_dots - dot;
if ( sys->ppu.hit_line == sys->ppu.scanline &&
(sys->core.memory.ppu.mask & (ppu_Mask_Sprite | ppu_Mask_Back)) &&
@@ -101,6 +109,7 @@ int nes_loop(nes* sys) {
dots_remaining = hit_dot - dot;
}
}

int cpu_cycles = (dots_remaining + 2) / 3;
dot += 3 * f6502_step(&sys->core, cpu_cycles);

@@ -111,7 +120,7 @@ int nes_loop(nes* sys) {
if (NULL != mem->mapper.hsync) {
mem->mapper.hsync(&mem->mapper);
}
status = nes_hsync(sys);
status = nes_hsync(sys, plat);
}
}



+ 1
- 1
src/nes.h View File

@@ -19,7 +19,7 @@ typedef struct {
void nes_init(nes*);
void nes_reset(nes*);
void nes_done(nes*);
int nes_loop(nes*);
int nes_loop(nes*, void*);


#endif // NES_H_

+ 2
- 2
src/nese.c View File

@@ -14,7 +14,7 @@ static int nese_file_size(FILE* file) {
}


int nese_start(nes* sys, const char* filename) {
int nese_start(nes* sys, const char* filename, void* plat) {
int status = 0;
void* cart_data = NULL;
int filesize = -1;
@@ -41,7 +41,7 @@ int nese_start(nes* sys, const char* filename) {

nes_reset(sys);

nes_loop(sys);
nes_loop(sys, plat);

if (cart_data) nese_unmap_file(cart_data, filesize);



+ 1
- 1
src/nese.h View File

@@ -4,7 +4,7 @@
#include "nes.h"


int nese_start(nes* sys, const char* filename);
int nese_start(nes* sys, const char* filename, void*);


#endif // NESE_H_

+ 3
- 3
src/port.h View File

@@ -9,9 +9,9 @@
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*);
int nese_line_ready(void*, uint8_t* buffer, int line);
int nese_frame_ready(void*);
int nese_update_input(void*, nes_Input*);


#endif // NESE_PORT_H_

+ 54
- 0
src/ppu.c View File

@@ -1,5 +1,10 @@
#include <string.h>

#include "ppu.h"

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


static const uint8_t mirror_schemes[][4] = {
[nes_Mirror_Horizontal] = {0, 0, 1, 1},
@@ -50,3 +55,52 @@ void nes_ppu_find_hit_line(nes_PPU* ppu, nes_PPU_Memory* mem) {
}
}
}


void nes_ppu_render_line(nes_PPU* ppu, nes_PPU_Memory* mem) {
uint8_t* ptr = ppu->line_data;

if (!(mem->mask & ppu_Mask_Back)) {
memset(ptr, 0, nes_ppu_render_w);
} else {
/*
int bank = nes_PPU_Nametable_Bank_Index +
((mem->addr >> 10) & 3);

int y_coarse = (mem->addr >> 5) & 0x1F;
int y_fine = mem->addr >> 12;
int x_coarse = mem->addr & 0x1FU;

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);

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


// TODO: Draw Background
}

// TODO: Draw Sprites

if (mem->mask & (ppu_Mask_Sprite | ppu_Mask_Back)) {
uint16_t mask = 0b10000011111;
mem->addr = (mem->addr & ~mask) | (mem->t & mask);

int y_scroll = (mem->addr >> 12) |
((mem->addr >> 2) & 0xF8U);
if (nes_ppu_render_h - 1 == y_scroll) {
y_scroll = 0;
mem->addr ^= 0x800;
} else if (0xFFU == y_scroll) {
y_scroll = 0;
} else {
++y_scroll;
}
mem->addr = (mem->addr & ~0b111001111100000) |
((y_scroll & 7) << 12) |
((y_scroll & 0xF8U) << 2);
}
}

+ 3
- 0
src/ppu.h View File

@@ -130,9 +130,12 @@ void nes_ppu_set_mirroring(nes_PPU_Memory* mem,
typedef struct {
int scanline;
int hit_line;
uint8_t line_data[nes_ppu_render_w];
} nes_PPU;

void nes_ppu_find_hit_line(nes_PPU*, nes_PPU_Memory*);

void nes_ppu_render_line(nes_PPU*, nes_PPU_Memory*);


#endif // NESE_PPU_H_

Loading…
Cancel
Save