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

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

int ines_load(nes_cart_rom* cart_rom, FILE* file) {
int ines_load(nes_cart* cart, FILE* file) {
int status = 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);
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");
status = -1;

} else if (1U == hdr.prg_size_lsb) {
// 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);
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");
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;
}

+ 1
- 1
src/ines.h View File

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

int ines_check(ines_Header*, FILE*);

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


#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_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) {


+ 8
- 2
src/nes.h View File

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

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

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

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_init(nes*);
int nes_init(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;
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;
if (status == 0) {
@@ -79,7 +79,10 @@ int main(int argc, char* argv[]) {
}

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

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

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 +
nes_ppu_mem_vram_size) {
// 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) {
// printf("PPU: BLANK READ %04x > %02x\n", ppu->addr, val);
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);
ppu->vram[vram_addr] = val;
nes_vram_write(ppu->vram_map, vram_addr, val);
}

ppu->addr += (ppu->control & ppu_Control_VRAM_Inc) ?
@@ -172,12 +174,17 @@ void nes_ppu_reset(nes_ppu* ppu) {
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->status = 0;
ppu->oam_addr = 0;
ppu->addr = 0;
ppu->vram_map = ( horizontal ?
&vram_horizontal :
&vram_vertical );
nes_ppu_reset(ppu);
return nes_vram_init(ppu->vram_map);
}

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

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 "vram.h"


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

// Timing
int frame;
@@ -108,7 +110,8 @@ typedef struct {
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_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 {
ppu_Result_Halt = -1,
@@ -119,6 +122,6 @@ typedef enum {
} nes_ppu_Result;

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

#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,
int xs, int ys, int w, int h) {
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;
index_line += xs + (ys * nes_ppu_blocks_w);
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