Browse Source

Add horizontal/vertical VRAM mirroring

- Ready to add support for mappers
 - Block-line background rendering presents certain artifacts in some
   vertical-scrolling games like 1942
master
Nathaniel Walizer 1 year ago
parent
commit
66460ee5fe
11 changed files with 193 additions and 30 deletions
  1. +1
    -1
      Makefile
  2. +15
    -5
      src/ines.c
  3. +1
    -1
      src/ines.h
  4. +4
    -2
      src/nes.c
  5. +8
    -2
      src/nes.h
  6. +5
    -2
      src/nese.c
  7. +11
    -13
      src/ppu.c
  8. +6
    -3
      src/ppu.h
  9. +2
    -1
      src/sdl_render.c
  10. +92
    -0
      src/vram.c
  11. +48
    -0
      src/vram.h

+ 1
- 1
Makefile View File

@@ -13,7 +13,7 @@ BINDIR = bin
TARGET_1 = nese TARGET_1 = nese
LDLIBS_1 = -lSDL2 LDLIBS_1 = -lSDL2


SRC_SRCS_1 = nese.c ines.c nes.c ppu.c input.c
SRC_SRCS_1 = nese.c ines.c nes.c ppu.c input.c vram.c
SRC_SRCS_1 += sdl_render.c sdl_input.c SRC_SRCS_1 += sdl_render.c sdl_input.c


EXT_SRCS_1 = e6502/e6502.c EXT_SRCS_1 = e6502/e6502.c


+ 15
- 5
src/ines.c View File

@@ -38,7 +38,7 @@ int ines_check(ines_Header* ret, FILE* file) {
return status; return status;
} }


int ines_load(nes_cart_rom* cart_rom, FILE* file) {
int ines_load(nes_cart* cart, FILE* file) {
int status = 0; int status = 0;
ines_Header hdr = {0}; ines_Header hdr = {0};


@@ -56,14 +56,14 @@ int ines_load(nes_cart_rom* cart_rom, FILE* file) {
prg_size, nes_mem_rom_size); prg_size, nes_mem_rom_size);
status = -1; status = -1;


} else if (1 != fread(cart_rom->prg, prg_size, 1, file)) {
} else if (1 != fread(cart->rom.prg, prg_size, 1, file)) {
INES_ERR("Failed to read program ROM"); INES_ERR("Failed to read program ROM");
status = -1; status = -1;


} else if (1U == hdr.prg_size_lsb) { } else if (1U == hdr.prg_size_lsb) {
// If there's only one PRG_ROM chunk, duplicate it // If there's only one PRG_ROM chunk, duplicate it
memcpy(cart_rom->prg + ines_prg_rom_chunk,
cart_rom, ines_prg_rom_chunk);
memcpy(cart->rom.prg + ines_prg_rom_chunk,
cart->rom.prg, ines_prg_rom_chunk);
} }
} }


@@ -74,11 +74,21 @@ int ines_load(nes_cart_rom* cart_rom, FILE* file) {
chr_size, nes_ppu_mem_size); chr_size, nes_ppu_mem_size);
status = -1; status = -1;


} else if (1 != fread(cart_rom->chr, chr_size, 1, file)) {
} else if (1 != fread(cart->rom.chr, chr_size, 1, file)) {
INES_ERR("Failed to read sprite ROM"); INES_ERR("Failed to read sprite ROM");
status = -1; status = -1;
} }
} }


if (0 == status) {
if (hdr.flags_6 & ines_Flag_Horizontal) {
cart->flags |= Cart_Flag_Horizontal;
} else {
cart->flags &= ~Cart_Flag_Horizontal;
}
cart->mapper = (hdr.flags_6 & ines_Mapper_Nibble_Lo) >> 4 |
(hdr.flags_7 & ines_Mapper_Nibble_Hi);
}

return status; return status;
} }

+ 1
- 1
src/ines.h View File

@@ -47,7 +47,7 @@ typedef struct {


int ines_check(ines_Header*, FILE*); int ines_check(ines_Header*, FILE*);


int ines_load(nes_cart_rom*, FILE*);
int ines_load(nes_cart*, FILE*);




#endif // INES_H_ #endif // INES_H_

+ 4
- 2
src/nes.c View File

@@ -69,10 +69,12 @@ void nes_mem_write(nes* sys, uint16_t addr, uint8_t val) {
} }
} }


void nes_init(nes* sys) {
int nes_init(nes* sys) {
e6502_init(&sys->cpu, (e6502_Read*)nes_mem_read, e6502_init(&sys->cpu, (e6502_Read*)nes_mem_read,
(e6502_Write*)nes_mem_write, sys); (e6502_Write*)nes_mem_write, sys);
nes_ppu_init(&sys->ppu, sys->cart.rom.chr);
return nes_ppu_init(&sys->ppu, sys->cart.rom.chr,
(sys->cart.flags & Cart_Flag_Horizontal),
sys->cart.mapper);
} }


void nes_reset(nes* sys) { void nes_reset(nes* sys) {


+ 8
- 2
src/nes.h View File

@@ -43,10 +43,16 @@ typedef struct {
uint8_t chr[nes_ppu_mem_size]; uint8_t chr[nes_ppu_mem_size];
} nes_cart_rom; } nes_cart_rom;


typedef enum {
Cart_Flag_Vertical = 0b0,
Cart_Flag_Horizontal = 0b1,
} nes_Cart_Flags;

typedef struct { typedef struct {
nes_cart_rom rom; nes_cart_rom rom;
uint8_t wram[nes_mem_wram_size]; uint8_t wram[nes_mem_wram_size];
// TODO: Mapper support
nes_Cart_Flags flags;
int mapper;
} nes_cart; } nes_cart;


typedef struct { typedef struct {
@@ -62,7 +68,7 @@ uint8_t nes_mem_read(nes*, uint16_t addr);


void nes_mem_write(nes*, uint16_t addr, uint8_t); void nes_mem_write(nes*, uint16_t addr, uint8_t);


void nes_init(nes*);
int nes_init(nes*);


void nes_reset(nes*); void nes_reset(nes*);




+ 5
- 2
src/nese.c View File

@@ -66,7 +66,7 @@ int main(int argc, char* argv[]) {
int n_loops = (argc > 1) ? atoi(argv[1]) : 0; int n_loops = (argc > 1) ? atoi(argv[1]) : 0;
if (n_loops <= 0) n_loops = INT_MAX; if (n_loops <= 0) n_loops = INT_MAX;


status = ines_load(&sys.cart.rom, stdin);
status = ines_load(&sys.cart, stdin);


nes_Renderer* rend = &sdl_renderer; nes_Renderer* rend = &sdl_renderer;
if (status == 0) { if (status == 0) {
@@ -79,7 +79,10 @@ int main(int argc, char* argv[]) {
} }


if (status == 0) { if (status == 0) {
nes_init(&sys);
status = nes_init(&sys);
}

if (status == 0) {
nes_reset(&sys); nes_reset(&sys);


nes_render(rend, &sys.ppu); nes_render(rend, &sys.ppu);


+ 11
- 13
src/ppu.c View File

@@ -45,8 +45,10 @@ uint8_t nes_ppu_read(nes_ppu* ppu, uint16_t addr) {
} else if (ppu->addr < nes_ppu_mem_vram_start + } else if (ppu->addr < nes_ppu_mem_vram_start +
nes_ppu_mem_vram_size) { nes_ppu_mem_vram_size) {
// printf("PPU: VRAM READ %04x > %02x\n", ppu->addr, val); // printf("PPU: VRAM READ %04x > %02x\n", ppu->addr, val);
ppu->data = ppu->vram[ppu->addr -
nes_ppu_mem_vram_start];
ppu->data = nes_vram_read(
ppu->vram_map,
ppu->addr - nes_ppu_mem_vram_start
);
} else if (ppu->addr < nes_ppu_mem_pal_start) { } else if (ppu->addr < nes_ppu_mem_pal_start) {
// printf("PPU: BLANK READ %04x > %02x\n", ppu->addr, val); // printf("PPU: BLANK READ %04x > %02x\n", ppu->addr, val);
ppu->data = ppu->chr_mem[ppu->addr]; ppu->data = ppu->chr_mem[ppu->addr];
@@ -152,7 +154,7 @@ void nes_ppu_write(nes_ppu* ppu, uint16_t addr, uint8_t val) {
} }
*/ */
// printf("PPU: VRAM %04x < %02x\n", vram_addr, val); // printf("PPU: VRAM %04x < %02x\n", vram_addr, val);
ppu->vram[vram_addr] = val;
nes_vram_write(ppu->vram_map, vram_addr, val);
} }


ppu->addr += (ppu->control & ppu_Control_VRAM_Inc) ? ppu->addr += (ppu->control & ppu_Control_VRAM_Inc) ?
@@ -172,12 +174,17 @@ void nes_ppu_reset(nes_ppu* ppu) {
ppu->cycle = 0; ppu->cycle = 0;
} }


void nes_ppu_init(nes_ppu* ppu, uint8_t* chr_mem) {
int nes_ppu_init(nes_ppu* ppu, uint8_t* chr_mem,
int horizontal, int mapper) {
ppu->chr_mem = chr_mem; ppu->chr_mem = chr_mem;
ppu->status = 0; ppu->status = 0;
ppu->oam_addr = 0; ppu->oam_addr = 0;
ppu->addr = 0; ppu->addr = 0;
ppu->vram_map = ( horizontal ?
&vram_horizontal :
&vram_vertical );
nes_ppu_reset(ppu); nes_ppu_reset(ppu);
return nes_vram_init(ppu->vram_map);
} }


int nes_ppu_run(nes_ppu* ppu, int cycles) { int nes_ppu_run(nes_ppu* ppu, int cycles) {
@@ -224,12 +231,3 @@ int nes_ppu_run(nes_ppu* ppu, int cycles) {


return result; return result;
} }

/*
int nes_ppu_cycles_til_vblank(nes_ppu* ppu) {
int cycles_til_vblank = nes_ppu_active_cycles - (
ppu->cycle + (ppu->scanline * nes_ppu_dots));
return (cycles_til_vblank > 0 ? cycles_til_vblank :
nes_ppu_vblank_cycles + cycles_til_vblank);
}
*/

+ 6
- 3
src/ppu.h View File

@@ -3,6 +3,8 @@


#include <stdint.h> #include <stdint.h>


#include "vram.h"



#define nes_ppu_dots (341U) #define nes_ppu_dots (341U)
#define nes_ppu_prerender (1U) #define nes_ppu_prerender (1U)
@@ -79,8 +81,8 @@ typedef struct {
// Memory // Memory
uint8_t* chr_mem; uint8_t* chr_mem;
uint8_t oam[nes_ppu_oam_size]; uint8_t oam[nes_ppu_oam_size];
uint8_t vram[nes_ppu_mem_vram_size];
uint8_t palette[nes_ppu_mem_pal_size]; uint8_t palette[nes_ppu_mem_pal_size];
nes_vram_map* vram_map;


// Timing // Timing
int frame; int frame;
@@ -108,7 +110,8 @@ typedef struct {
uint8_t nes_ppu_read(nes_ppu* ppu, uint16_t addr); uint8_t nes_ppu_read(nes_ppu* ppu, uint16_t addr);
void nes_ppu_write(nes_ppu* ppu, uint16_t addr, uint8_t val); void nes_ppu_write(nes_ppu* ppu, uint16_t addr, uint8_t val);
void nes_ppu_reset(nes_ppu* ppu); void nes_ppu_reset(nes_ppu* ppu);
void nes_ppu_init(nes_ppu* ppu, uint8_t* chr_mem);
int nes_ppu_init(nes_ppu* ppu, uint8_t* chr_mem,
int horizonal, int mapper);


typedef enum { typedef enum {
ppu_Result_Halt = -1, ppu_Result_Halt = -1,
@@ -119,6 +122,6 @@ typedef enum {
} nes_ppu_Result; } nes_ppu_Result;


nes_ppu_Result nes_ppu_run(nes_ppu* ppu, int cycles); nes_ppu_Result nes_ppu_run(nes_ppu* ppu, int cycles);
//int nes_ppu_cycles_til_vblank(nes_ppu* ppu);


#endif // ENES_PPU_H_ #endif // ENES_PPU_H_

+ 2
- 1
src/sdl_render.c View File

@@ -197,7 +197,8 @@ static void render_background_area(const nes_ppu* ppu, int page,
void* buffer, int pitch, void* buffer, int pitch,
int xs, int ys, int w, int h) { int xs, int ys, int w, int h) {
int bank = (ppu->control & ppu_Control_Back_Bank) ? 0x100 : 0; int bank = (ppu->control & ppu_Control_Back_Bank) ? 0x100 : 0;
const uint8_t* index_line = &ppu->vram[page * 0x400U];
const uint8_t* index_line = nes_vram_page(ppu->vram_map,
page);
const uint8_t* attrs = index_line + 960U; const uint8_t* attrs = index_line + 960U;
index_line += xs + (ys * nes_ppu_blocks_w); index_line += xs + (ys * nes_ppu_blocks_w);
uint8_t* dst_line = (uint8_t*)buffer; uint8_t* dst_line = (uint8_t*)buffer;


+ 92
- 0
src/vram.c View File

@@ -0,0 +1,92 @@
#include <stdlib.h>

#include "vram.h"


typedef enum {
Mirror_Method_Vertical,
Mirror_Method_Horizontal,
} vram_Mirror_Method;

typedef struct {
vram_Mirror_Method method;
uint8_t vram[nes_vram_page_size * 2];
} vram_mirror;


static vram_mirror* vram_mirror_create(vram_Mirror_Method method) {
vram_mirror* mirror = calloc(1, sizeof(vram_mirror));
if (NULL != mirror) mirror->method = method;
return mirror;
}

static void vram_mirror_destroy(vram_mirror* mirror) {
free(mirror);
}

static inline uint8_t* vram_mirror_page(vram_mirror* mirror,
int page) {
if (mirror->method == Mirror_Method_Vertical) {
page >>= 1;
}
return &mirror->vram[(page & 1) ? nes_vram_page_size : 0];
}

static inline uint8_t* vram_mirror_map(vram_mirror* mirror,
uint16_t addr) {
uint8_t* page = vram_mirror_page(mirror, (addr >> 10));
return &page[addr & nes_vram_addr_mask];
}

static int vram_map_mirror_init_method(nes_vram_map* map,
vram_Mirror_Method method) {
map->data = vram_mirror_create(method);
return (NULL == map->data ? -1 : 0);
}

static int vram_map_mirror_init_horz(nes_vram_map* map) {
return vram_map_mirror_init_method(
map, Mirror_Method_Horizontal
);
}

static int vram_map_mirror_init_vert(nes_vram_map* map) {
return vram_map_mirror_init_method(
map, Mirror_Method_Vertical
);
}

static void vram_map_mirror_done(nes_vram_map* map) {
vram_mirror_destroy((vram_mirror*)map->data);
}

static uint8_t vram_map_mirror_read(nes_vram_map* map,
uint16_t addr) {
return *(vram_mirror_map((vram_mirror*)map->data, addr));
}

static void vram_map_mirror_write(nes_vram_map* map,
uint16_t addr,
uint8_t val) {
*(vram_mirror_map((vram_mirror*)map->data, addr)) = val;
}

static uint8_t* vram_map_mirror_page(nes_vram_map* map, int page) {
return vram_mirror_page((vram_mirror*)map->data, page);
}

nes_vram_map vram_horizontal = {
.init = vram_map_mirror_init_horz,
.done = vram_map_mirror_done,
.read = vram_map_mirror_read,
.write = vram_map_mirror_write,
.page = vram_map_mirror_page,
};

nes_vram_map vram_vertical = {
.init = vram_map_mirror_init_vert,
.done = vram_map_mirror_done,
.read = vram_map_mirror_read,
.write = vram_map_mirror_write,
.page = vram_map_mirror_page,
};

+ 48
- 0
src/vram.h View File

@@ -0,0 +1,48 @@
#ifndef NES_VRAM_
#define NES_VRAM_

#include <stdint.h>


#define nes_vram_page_size (0x400U)
#define nes_vram_page_mask (0xC00U)
#define nes_vram_addr_mask (0x3FFU)


typedef struct nes_vram_map_t {
int (*init)(struct nes_vram_map_t*);
void (*done)(struct nes_vram_map_t*);
uint8_t* (*page)(struct nes_vram_map_t*, int page);
uint8_t (*read)(struct nes_vram_map_t*, uint16_t addr);
void (*write)(struct nes_vram_map_t*, uint16_t addr, uint8_t val);
void* data;
} nes_vram_map;

static inline int nes_vram_init(nes_vram_map* map) {
return map->init(map);
}

static inline void nes_vram_done(nes_vram_map* map) {
map->done(map);
}

static inline uint8_t* nes_vram_page(nes_vram_map* map, int page) {
return map->page(map, page);
}

static inline uint8_t nes_vram_read(nes_vram_map* map,
uint16_t addr) {
return map->read(map, addr);
}

static inline void nes_vram_write(nes_vram_map* map,
uint16_t addr, uint8_t val) {
map->write(map, addr, val);
}


extern nes_vram_map vram_horizontal;
extern nes_vram_map vram_vertical;


#endif // NES_VRAM_

Loading…
Cancel
Save