Просмотр исходного кода

Add basic mapper support

master
Nathaniel Walizer 1 год назад
Родитель
Сommit
bfb9e3df5f
16 измененных файлов: 354 добавлений и 115 удалений
  1. +12
    -4
      Makefile
  2. +114
    -0
      src/cart.c
  3. +35
    -0
      src/cart.h
  4. +12
    -68
      src/ines.c
  5. +12
    -2
      src/ines.h
  6. +2
    -0
      src/map/map.h
  7. +8
    -0
      src/map/mmc1.c
  8. +62
    -0
      src/map/nrom.c
  9. +11
    -0
      src/mapper.c
  10. +47
    -0
      src/mapper.h
  11. +7
    -5
      src/nes.c
  12. +2
    -19
      src/nes.h
  13. +2
    -2
      src/nese.c
  14. +9
    -6
      src/ppu.c
  15. +9
    -6
      src/ppu.h
  16. +10
    -3
      src/sdl_render.c

+ 12
- 4
Makefile Просмотреть файл

@@ -1,7 +1,8 @@
CC = gcc
LD = $(CC)
CFLAGS = -g -Wall -Werror -Wshadow -I.. #-DE6502_DEBUG
LDFLAGS =
PFLAGS = -g
CFLAGS = $(PFLAGS) -Wall -Werror -Wshadow -I..
LDFLAGS = $(PFLAGS)

OBJDIR = obj
SRCDIR = src
@@ -13,13 +14,20 @@ BINDIR = bin
TARGET_1 = nese
LDLIBS_1 = -lSDL2

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

MAPDIR = src/map
MAP_SRCS_1 = nrom.c mmc1.c

EXT_SRCS_1 = e6502/e6502.c


SRCS_1 = $(SRC_SRCS_1:%=$(SRCDIR)/%) $(EXT_SRCS_1)
SRCS_1 = $(SRC_SRCS_1:%=$(SRCDIR)/%)
SRCS_1 += $(MAP_SRCS_1:%=$(MAPDIR)/%)
SRCS_1 += $(EXT_SRCS_1)
OBJS_1 = $(SRCS_1:%.c=$(OBJDIR)/%.o)




+ 114
- 0
src/cart.c Просмотреть файл

@@ -0,0 +1,114 @@
#include <sys/mman.h>

#include "cart.h"
#include "ines.h"
#include "mapper.h"


int nes_cart_init_mem(nes_cart* cart, void* mem, int len) {
int status = 0;
ines_Header *hdr = (ines_Header*)mem;
void* ptr = &hdr[1];

status = ines_check_mem(hdr);

if (0 == status && (hdr->flags_6 & ines_Flag_Trainer)) {
// Skip trainer
ptr += ines_trainer_size;
}

if (0 == status) {
int prg_size = ines_prg_rom_chunk * hdr->prg_size_lsb;
if (prg_size > nes_mem_rom_size || prg_size <= 0) {
INES_ERR("Bad program ROM size: %d / %d",
prg_size, nes_mem_rom_size);
status = -1;
} else {
cart->prg_rom = ptr;
cart->prg_rom_size = prg_size;
}
ptr += prg_size;
}

if (0 == status) {
int chr_size = ines_chr_rom_chunk * hdr->chr_size_lsb;
if (chr_size > nes_ppu_mem_size || chr_size <= 0) {
INES_ERR("Bad sprite ROM size: %d / %d",
chr_size, nes_ppu_mem_size);
status = -1;

} else {
cart->chr_rom = ptr;
cart->chr_rom_size = chr_size;
}
ptr += chr_size;
}

if (0 == status) {
int index = (hdr->flags_6 & ines_Mapper_Nibble_Lo) >> 4 |
(hdr->flags_7 & ines_Mapper_Nibble_Hi);
cart->mapper = nes_mappers[index];
if (NULL == cart->mapper) {
INES_ERR("No mapper found for type %d", index);
status = -1;
}
}

if (0 == status) {
status = nes_map_init(cart->mapper, cart);
}

if (0 == status) {
if (hdr->flags_6 & ines_Flag_Horizontal) {
cart->flags |= Cart_Flag_Horizontal;
} else {
cart->flags &= ~Cart_Flag_Horizontal;
}

cart->ines_mem = mem;
cart->ines_size = len;
}

return status;
}

void nes_cart_done(nes_cart* cart) {
if (NULL != cart->ines_mem) {
munmap(cart->ines_mem, cart->ines_size);
cart->ines_mem = NULL;
}
}

int nes_cart_init_file(nes_cart* cart, FILE* file) {
int status = 0;
int size = -1;
void* mem = NULL;

// Get file size
status = fseek(file, 0, SEEK_END);
if (0 != status) {
INES_ERR("Failed to check file size");
} else {
size = ftell(file);
}

// Map file
if (0 == status) {
mem = mmap(NULL, size, PROT_READ, MAP_PRIVATE,
fileno(file), 0);
if (NULL == mem) {
INES_ERR("Failed to map file (%d bytes)", size);
status = -1;
}
}

// Check in memory; unmap on failure
if (0 == status) {
status = nes_cart_init_mem(cart, mem, size);
if (0 != status) {
munmap(mem, size);
}
}

return status;
}

+ 35
- 0
src/cart.h Просмотреть файл

@@ -0,0 +1,35 @@
#ifndef NES_CART_H_
#define NES_CART_H_

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


#define nes_mem_wram_size (0x2000U)


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

typedef struct nes_cart_t {
uint8_t* prg_rom;
int prg_rom_size;
uint8_t* chr_rom;
int chr_rom_size;
uint8_t wram[nes_mem_wram_size];
nes_Cart_Flags flags;
struct nes_mapper_t* mapper;

void* ines_mem;
int ines_size;
} nes_cart;


int nes_cart_init_file(nes_cart*, FILE* file);
int nes_cart_init_mem(nes_cart*, void*, int len);
void nes_cart_done(nes_cart*);


#endif // NES_CART_H_

+ 12
- 68
src/ines.c Просмотреть файл

@@ -4,90 +4,34 @@
#include "nes.h"


#define INES_LOG(tag, fmt, ...) \
fprintf(stderr, tag ": iNES: " fmt "\n" __VA_OPT__(,) __VA_ARGS__)
#define INES_ERR(...) INES_LOG("E", __VA_ARGS__)


#define ines_trainer_size (512U)
#define ines_prg_rom_chunk (16384U)
#define ines_chr_rom_chunk (8192U)


int ines_check(ines_Header* ret, FILE* file) {
int ines_check_mem(void* mem) {
int status = 0;
ines_Header hdr = {0};
ines_Header *hdr = (ines_Header*)mem;

if (1 != fread(&hdr, sizeof(ines_Header), 1, file)) {
INES_ERR("Failed to read header");
status = -1;
}

if (0 == status && 0 != memcmp(hdr.magic,
ines_magic,
sizeof(hdr.magic))) {
if (0 != memcmp(hdr->magic, ines_magic, sizeof(hdr->magic))) {
INES_ERR("Bad file magic: expected '%.4s', got '%.4s'",
ines_magic, hdr.magic);
status =-1;
}

if (0 == status && NULL != ret) {
*ret = hdr;
ines_magic, hdr->magic);
status = -1;
}

return status;
}

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

status = ines_check(&hdr, file);

if (0 == status && (hdr.flags_6 & ines_Flag_Trainer)) {
// Skip trainer
status = fseek(file, ines_trainer_size, SEEK_CUR);
}

if (0 == status) {
int prg_size = ines_prg_rom_chunk * hdr.prg_size_lsb;
if (prg_size > nes_mem_rom_size) {
INES_ERR("Program ROM too large: %d > %d",
prg_size, nes_mem_rom_size);
status = -1;

} 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.prg, ines_prg_rom_chunk);
}
if (1 != fread(&hdr, sizeof(ines_Header), 1, file)) {
INES_ERR("Failed to read header");
status = -1;
}

if (0 == status) {
int chr_size = ines_chr_rom_chunk * hdr.chr_size_lsb;
if (chr_size > nes_ppu_mem_size) {
INES_ERR("Sprite ROM too large: %d > %d",
chr_size, nes_ppu_mem_size);
status = -1;

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

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);
if (0 == status && NULL != ret) {
*ret = hdr;
}

return status;


+ 12
- 2
src/ines.h Просмотреть файл

@@ -7,6 +7,16 @@
#include "nes.h"


#define INES_LOG(tag, fmt, ...) \
fprintf(stderr, tag ": iNES: " fmt "\n" __VA_OPT__(,) __VA_ARGS__)
#define INES_ERR(...) INES_LOG("E", __VA_ARGS__)


#define ines_trainer_size (512U)
#define ines_prg_rom_chunk (16384U)
#define ines_chr_rom_chunk (8192U)


#define ines_magic "NES\x1a"

typedef enum {
@@ -45,9 +55,9 @@ typedef struct {
} __attribute__ (( packed )) ines_Header;


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

int ines_load(nes_cart*, FILE*);
int ines_check_mem(void*);


#endif // INES_H_

+ 2
- 0
src/map/map.h Просмотреть файл

@@ -0,0 +1,2 @@
#include "../mapper.h"
#include "../cart.h"

+ 8
- 0
src/map/mmc1.c Просмотреть файл

@@ -0,0 +1,8 @@
#include "map.h"

// Stub


nes_mapper mapper_mmc1 = {

};

+ 62
- 0
src/map/nrom.c Просмотреть файл

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

#include "map.h"


typedef struct {
uint8_t* prg_rom;
int prg_rom_size;
uint8_t* prg_ram;
int prg_ram_size;
uint8_t* chr_rom;
int chr_rom_size;
} nrom_mapper;

// TODO: PRG_ROM region mirroring

int nrom_init(nes_mapper* nes_map, nes_cart* cart) {
nrom_mapper* map = malloc(sizeof(nrom_mapper));
nes_map->data = map;
if (NULL != map) {
map->prg_rom = cart->prg_rom;
map->prg_rom_size = cart->prg_rom_size;
map->chr_rom = cart->chr_rom;
map->chr_rom_size = cart->chr_rom_size;
map->prg_ram = cart->wram;
map->prg_ram_size = nes_mem_wram_size;
}
return (NULL == map ? -1 : 0);
}

void nrom_done(nes_mapper* nes_map) {
free(nes_map->data);
}

static inline uint8_t* nrom_prg_addr(nrom_mapper* map,
uint16_t addr) {
if (addr > map->prg_rom_size) {
addr &= 0x3FFF;
}
return &(map->prg_rom[addr]);
}

uint8_t nrom_prg_read(nes_mapper* map, uint16_t addr) {
return *(nrom_prg_addr((nrom_mapper*)map->data, addr));
}

void nrom_prg_write(nes_mapper* map, uint16_t addr, uint8_t val) {
// No ROM writes.
}

uint8_t* nrom_chr_addr(nes_mapper* nes_map, uint16_t addr) {
nrom_mapper* map = (nrom_mapper*)nes_map->data;
return &map->chr_rom[addr % map->chr_rom_size];
}

nes_mapper mapper_nrom = {
.init = nrom_init,
.done = nrom_done,
.prg_read = nrom_prg_read,
.prg_write = nrom_prg_write,
.chr_addr = nrom_chr_addr,
};

+ 11
- 0
src/mapper.c Просмотреть файл

@@ -0,0 +1,11 @@
#include "mapper.h"


extern nes_mapper mapper_nrom;
extern nes_mapper mapper_mmc1;


nes_mapper* nes_mappers[256] = {
[ 0] = &mapper_nrom,
[ 1] = &mapper_mmc1,
};

+ 47
- 0
src/mapper.h Просмотреть файл

@@ -0,0 +1,47 @@
#ifndef NES_MAPPER_H_
#define NES_MAPPER_H_

#include <stdint.h>


struct nes_cart_t;

typedef struct nes_mapper_t {
int (*init)(struct nes_mapper_t*, struct nes_cart_t* cart);
void (*done)(struct nes_mapper_t*);
uint8_t (*prg_read)(struct nes_mapper_t*, uint16_t addr);
void (*prg_write)(struct nes_mapper_t*, uint16_t addr, uint8_t val);
uint8_t* (*chr_addr)(struct nes_mapper_t*, uint16_t addr);
void* data;
} nes_mapper;

static inline int nes_map_init(nes_mapper* map,
struct nes_cart_t* cart) {
return map->init(map, cart);
}

static inline void nes_map_done(nes_mapper* map) {
map->done(map);
}

static inline uint8_t nes_map_prg_read(nes_mapper* map,
uint16_t addr) {
return map->prg_read(map, addr);
}

static inline void nes_map_prg_write(nes_mapper* map,
uint16_t addr,
uint8_t val) {
map->prg_write(map, addr, val);
}

static inline uint8_t* nes_map_chr_addr(nes_mapper* map,
uint16_t addr) {
return map->chr_addr(map, addr);
}


extern nes_mapper* nes_mappers[];


#endif

+ 7
- 5
src/nes.c Просмотреть файл

@@ -1,6 +1,7 @@
#include <stdio.h>

#include "nes.h"
#include "mapper.h"


uint8_t nes_mem_read(nes* sys, uint16_t addr) {
@@ -25,10 +26,12 @@ uint8_t nes_mem_read(nes* sys, uint16_t addr) {
// TODO: Expansion ROM support

} else if (addr < nes_mem_rom_start) {
// TODO: Send to mapper?
val = sys->cart.wram[addr - nes_mem_wram_start];

} else {
val = sys->cart.rom.prg[addr - nes_mem_rom_start];
val = nes_map_prg_read(sys->cart.mapper,
addr - nes_mem_rom_start);
}

return val;
@@ -65,16 +68,15 @@ void nes_mem_write(nes* sys, uint16_t addr, uint8_t val) {
sys->cart.wram[addr - nes_mem_wram_start] = val;

} else {
// No ROM writes
nes_map_prg_write(sys->cart.mapper,
addr - nes_mem_rom_start, val);
}
}

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

void nes_reset(nes* sys) {


+ 2
- 19
src/nes.h Просмотреть файл

@@ -1,6 +1,7 @@
#ifndef NES_H_
#define NES_H_

#include "cart.h"
#include "apu.h"
#include "ppu.h"
#include "input.h"
@@ -22,7 +23,7 @@
#define nes_mem_exp_start (0x4020U)
#define nes_mem_exp_size (0x1FE0U)
#define nes_mem_wram_start (0x6000U)
#define nes_mem_wram_size (0x2000U)
//#define nes_mem_wram_size (0x2000U)
#define nes_mem_rom_start (0x8000U)
#define nes_mem_rom_size (0x8000U)

@@ -37,24 +38,6 @@
#define nes_apu_map_size (0x20U)


typedef struct {
// TODO: Dynamic size support
uint8_t prg[nes_mem_rom_size];
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];
nes_Cart_Flags flags;
int mapper;
} nes_cart;

typedef struct {
e6502_Core cpu;
nes_ppu ppu;


+ 2
- 2
src/nese.c Просмотреть файл

@@ -2,7 +2,7 @@
#include <stdlib.h>
#include <time.h>

#include "ines.h"
#include "nes.h"
#include "render.h"
#include "input.h"

@@ -68,7 +68,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, stdin);
status = nes_cart_init_file(&sys.cart, stdin);

nes_Renderer* rend = &sdl_renderer;
if (status == 0) {


+ 9
- 6
src/ppu.c Просмотреть файл

@@ -1,6 +1,8 @@
#include <stdio.h>

#include "ppu.h"
#include "mapper.h"
#include "cart.h"


// TODO: Retain open bus bits?
@@ -41,7 +43,8 @@ uint8_t nes_ppu_read(nes_ppu* ppu, uint16_t addr) {

if (ppu->addr < nes_ppu_mem_vram_start) {
// printf("PPU: CHR MEM READ %04x > %02x\n", ppu->addr, val);
ppu->data = ppu->chr_mem[ppu->addr];
ppu->data = *(nes_map_chr_addr(ppu->mapper,
ppu->addr));
} else if (ppu->addr < nes_ppu_mem_vram_start +
nes_ppu_mem_vram_size) {
VRAM_LOG("PPU: VRAM READ %04x > %02x\n", ppu->addr, val);
@@ -51,7 +54,8 @@ uint8_t nes_ppu_read(nes_ppu* ppu, uint16_t addr) {
);
} 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];
ppu->data = *(nes_map_chr_addr(ppu->mapper,
ppu->addr));
}
}

@@ -174,13 +178,12 @@ void nes_ppu_reset(nes_ppu* ppu) {
ppu->cycle = 0;
}

int nes_ppu_init(nes_ppu* ppu, uint8_t* chr_mem,
int horizontal, int mapper) {
ppu->chr_mem = chr_mem;
int nes_ppu_init(nes_ppu* ppu, const nes_cart* cart) {
ppu->mapper = cart->mapper;
ppu->status = 0;
ppu->oam_addr = 0;
ppu->addr = 0;
ppu->vram_map = ( horizontal ?
ppu->vram_map = ( (cart->flags & Cart_Flag_Horizontal) ?
&vram_horizontal :
&vram_vertical );
nes_ppu_reset(ppu);


+ 9
- 6
src/ppu.h Просмотреть файл

@@ -1,11 +1,15 @@
#ifndef ENES_PPU_H_
#define ENES_PPU_H_
#ifndef NES_PPU_H_
#define NES_PPU_H_

#include <stdint.h>

#include "vram.h"


struct nes_mapper_t;
struct nes_cart_t;


#define DBG_LOG(...) printf(__VA_ARGS__)

#ifdef DEBUG_OAM
@@ -103,7 +107,7 @@ typedef enum {

typedef struct {
// Memory
uint8_t* chr_mem;
struct nes_mapper_t* mapper;
uint8_t oam[nes_ppu_oam_size];
uint8_t palette[nes_ppu_mem_pal_size];
nes_vram_map* vram_map;
@@ -134,8 +138,7 @@ 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);
int nes_ppu_init(nes_ppu* ppu, uint8_t* chr_mem,
int horizonal, int mapper);
int nes_ppu_init(nes_ppu* ppu, const struct nes_cart_t*);

typedef enum {
ppu_Result_Halt = -1,
@@ -148,4 +151,4 @@ typedef enum {
nes_ppu_Result nes_ppu_run(nes_ppu* ppu, int cycles);


#endif // ENES_PPU_H_
#endif // NES_PPU_H_

+ 10
- 3
src/sdl_render.c Просмотреть файл

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

#include "render.h"
#include "ppu.h"
#include "mapper.h"


static SDL_Color nes_palette[64] = {
@@ -27,6 +28,12 @@ static SDL_Color nes_palette[64] = {
};


static inline uint8_t* chr_mem(const nes_ppu* ppu,
uint16_t addr) {
return ppu->mapper->chr_addr(ppu->mapper, addr);
}


typedef struct {
SDL_Window* window;
SDL_Renderer* renderer;
@@ -164,7 +171,7 @@ static void sdl_render_done(nes_Renderer* rend) {
static void render_bg_sprite(const nes_ppu* ppu, int index,
const uint8_t* pal,
void* loc, int pitch) {
uint8_t* sprite = &ppu->chr_mem[index * 16U];
uint8_t* sprite = chr_mem(ppu, index * 16U);
uint8_t* dst_line = (uint8_t*)loc;

for (int y = 8; y > 0; --y) {
@@ -263,7 +270,7 @@ typedef enum {
static void render_sprite(nes_ppu* ppu, int index,
const uint8_t* pal, uint8_t attr,
void* loc, int pitch) {
uint8_t* sprite = &ppu->chr_mem[index * 16U];
uint8_t* sprite = chr_mem(ppu, index * 16U);
uint8_t* dst_line = (uint8_t*)loc;
int dx = 1;
if (attr & oam_Attr_Flip_X) {
@@ -407,7 +414,7 @@ static void update_sprite_hit(nes_ppu* ppu, int block_line,
if (ppu->control & ppu_Control_Sprite_Bank) {
index += 0x100U;
}
const uint8_t* chr = &ppu->chr_mem[index * 16U];
const uint8_t* chr = chr_mem(ppu, index * 16U);
int render_line = block_line * 8U;
int start_y = (sprite->y + 1) + y_fine - render_line;
int end_y = start_y + 8;


Загрузка…
Отмена
Сохранить