From 39715ca6bf65d2e2dd889cdef4b39d584464d9e7 Mon Sep 17 00:00:00 2001 From: Peter Fors Date: Sun, 6 Apr 2025 12:27:12 +0200 Subject: added more mappers (buggy) --- audio.c | 2 +- build.sh | 7 +- callbacks.c | 25 +++++ cpu.c | 7 +- cpu_opcodes.c | 1 - dump_mappers.c | 112 +++++++++++++++++++ mapper.c | 24 ++-- mapper.h | 11 +- mapper_0003.c | 25 +++++ mapper_0003.h | 4 + mapper_0007.c | 44 ++++++++ mapper_0007.h | 7 ++ mapper_000b.c | 27 +++++ mapper_000b.h | 6 + mapper_2002.c | 28 +++++ mapper_2002.h | 7 ++ memory.c | 21 +++- mknes.c | 139 ++++++++++++----------- mknes.h | 7 ++ ppu.c | 347 ++++++++++++++++++++++++++++++++++----------------------- 20 files changed, 627 insertions(+), 224 deletions(-) create mode 100644 dump_mappers.c create mode 100644 mapper_0003.c create mode 100644 mapper_0003.h create mode 100644 mapper_0007.c create mode 100644 mapper_0007.h create mode 100644 mapper_000b.c create mode 100644 mapper_000b.h create mode 100644 mapper_2002.c create mode 100644 mapper_2002.h diff --git a/audio.c b/audio.c index 4311d2f..587065c 100644 --- a/audio.c +++ b/audio.c @@ -199,7 +199,7 @@ int audio_initialize(void) { params[0] = fmt_param; params[1] = buf_param; - int res = pw_stream_connect(pa_stream, PW_DIRECTION_OUTPUT, PW_ID_ANY, (pw_stream_flags)(PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_RT_PROCESS | PW_STREAM_FLAG_MAP_BUFFERS), params, 2); + int res = pw_stream_connect(pa_stream, PW_DIRECTION_OUTPUT, PW_ID_ANY, (PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_RT_PROCESS | PW_STREAM_FLAG_MAP_BUFFERS), params, 2); pw_thread_loop_unlock(pa_thread_loop); return 0; diff --git a/build.sh b/build.sh index 845da73..b548675 100755 --- a/build.sh +++ b/build.sh @@ -4,11 +4,12 @@ PROJECT_NAME="mknes" # Change this for each new project # Base configuration common to all builds -CFLAGS="-std=gnu++23 " +CFLAGS="-std=gnu11 " CFLAGS+="-mavx2 -mbmi2 -mtune=native -mfunction-return=keep -mindirect-branch=keep " CFLAGS+="-fwrapv -ffast-math -fno-trapping-math -fwhole-program " CFLAGS+="-fno-stack-protector -fno-PIE -no-pie -fno-strict-aliasing -ffunction-sections -fdata-sections " -CFLAGS+="-fno-exceptions -fno-rtti -fno-use-cxa-atexit -fno-non-call-exceptions -fno-unwind-tables -fno-asynchronous-unwind-tables " +# CFLAGS+="-fno-exceptions -fno-rtti -fno-use-cxa-atexit " +CFLAGS+="-fno-non-call-exceptions -fno-unwind-tables -fno-asynchronous-unwind-tables " CFLAGS+="-Wall -Wextra " CFLAGS+="-Wno-unused-parameter -Wno-sign-compare -Wno-trigraphs -Wno-maybe-uninitialized " CFLAGS+="-Wno-unused-variable -Wno-unused-const-variable -Wno-unused-function -Wno-write-strings -Wno-missing-field-initializers " @@ -61,7 +62,7 @@ set -e # Build Linux version ( # ../bin/ctime -begin .${PROJECT_NAME}_linux - g++ $CFLAGS ${PROJECT_NAME}.c -o ${PROJECT_NAME} $INCLUDE_PATHS $LINUX_INCLUDE $LDFLAGS $LINUX_LIBS + gcc $CFLAGS ${PROJECT_NAME}.c -o ${PROJECT_NAME} $INCLUDE_PATHS $LINUX_INCLUDE $LDFLAGS $LINUX_LIBS # ../bin/ctime -end .${PROJECT_NAME}_linux $? ) & diff --git a/callbacks.c b/callbacks.c index 806fe86..a610a43 100644 --- a/callbacks.c +++ b/callbacks.c @@ -43,6 +43,7 @@ static void toggle_fullscreen(bool enable) { } static void key_callback(GLFWwindow *window, int key, int scancode, int action, int mods) { + struct nes_state *nes_state = (struct nes_state*)glfwGetWindowUserPointer(window); if(key == GLFW_KEY_ESCAPE) { if(action == GLFW_PRESS) { @@ -50,6 +51,30 @@ static void key_callback(GLFWwindow *window, int key, int scancode, int action, } } + if(action == GLFW_PRESS) { + switch(key) { + case GLFW_KEY_X: nes_state->input[0] |= (1 << 0); break; // A + case GLFW_KEY_Z: nes_state->input[0] |= (1 << 1); break; // B + case GLFW_KEY_SPACE: nes_state->input[0] |= (1 << 2); break; // Select + case GLFW_KEY_ENTER: nes_state->input[0] |= (1 << 3); break; // Start + case GLFW_KEY_UP: nes_state->input[0] |= (1 << 4); break; + case GLFW_KEY_DOWN: nes_state->input[0] |= (1 << 5); break; + case GLFW_KEY_LEFT: nes_state->input[0] |= (1 << 6); break; + case GLFW_KEY_RIGHT: nes_state->input[0] |= (1 << 7); break; + } + } else if(action == GLFW_RELEASE) { + switch(key) { + case GLFW_KEY_X: nes_state->input[0] &= ~(1 << 0); break; + case GLFW_KEY_Z: nes_state->input[0] &= ~(1 << 1); break; + case GLFW_KEY_SPACE: nes_state->input[0] &= ~(1 << 2); break; + case GLFW_KEY_ENTER: nes_state->input[0] &= ~(1 << 3); break; + case GLFW_KEY_UP: nes_state->input[0] &= ~(1 << 4); break; + case GLFW_KEY_DOWN: nes_state->input[0] &= ~(1 << 5); break; + case GLFW_KEY_LEFT: nes_state->input[0] &= ~(1 << 6); break; + case GLFW_KEY_RIGHT: nes_state->input[0] &= ~(1 << 7); break; + } + } + if(action == GLFW_RELEASE) { switch(key) { case GLFW_KEY_F12: { diff --git a/cpu.c b/cpu.c index 263ee6e..34d5bed 100644 --- a/cpu.c +++ b/cpu.c @@ -20,7 +20,6 @@ static inline void unpack_flags(struct cpu_state *cpu, uint8_t value) { cpu->c = value & 1; } - static inline void update_zn(struct cpu_state *cpu, uint8_t result) { cpu->z = (result == 0); cpu->n = (result & 0x80) != 0; @@ -35,6 +34,7 @@ struct addr_result { #include "cpu_opcodes.c" #include "cpu_opcodes_ud.c" +__attribute__((hot)) static inline void do_nmi(struct nes_state * restrict state) { struct cpu_state * restrict cpu = &state->cpu; @@ -54,6 +54,7 @@ static inline void do_nmi(struct nes_state * restrict state) { cpu->i = 1; } +__attribute__((hot)) static inline void do_irq(struct nes_state * restrict state) { struct cpu_state * restrict cpu = &state->cpu; @@ -73,6 +74,7 @@ static inline void do_irq(struct nes_state * restrict state) { cpu->i = 1; } +__attribute__((hot)) static inline void check_interrupts(struct nes_state * restrict state) { struct cpu_state * restrict cpu = &state->cpu; @@ -85,6 +87,7 @@ static inline void check_interrupts(struct nes_state * restrict state) { } } +__attribute__((hot)) static void cpu_tick(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; @@ -92,7 +95,7 @@ static void cpu_tick(struct nes_state *state) { uint8_t opcode; - // printf("%5.5d %4.4x: ", line++, cpu->pc); + // printf("%4.4x: ", cpu->pc); opcode = memory_read(state, cpu->pc++); // printf("%2.2x a:%2.2x x:%2.2x y:%2.2x p:%2.2x sp:%2.2x cycle: %ld\n", opcode, cpu->a, cpu->x, cpu->y, pack_flags(cpu), cpu->sp, state->cycles); opcode_lut[opcode](state); diff --git a/cpu_opcodes.c b/cpu_opcodes.c index 5fd5634..b879015 100644 --- a/cpu_opcodes.c +++ b/cpu_opcodes.c @@ -583,7 +583,6 @@ static void opcode_sei(struct nes_state * restrict state) { // CMP - static inline void cmp(struct cpu_state * restrict cpu, uint8_t value) { uint8_t result = cpu->a - value; cpu->c = (cpu->a >= value); diff --git a/dump_mappers.c b/dump_mappers.c new file mode 100644 index 0000000..42034de --- /dev/null +++ b/dump_mappers.c @@ -0,0 +1,112 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define INES_HEADER_SIZE 16 + +static void process_nes_file(const char *path) { + FILE *f = fopen(path, "rb"); + if(!f) return; + + uint8_t header[INES_HEADER_SIZE]; + if(fread(header, 1, INES_HEADER_SIZE, f) != INES_HEADER_SIZE) { + fclose(f); + return; + } + fclose(f); + + if(memcmp(header, "NES\x1A", 4) != 0) return; + + uint8_t mapper_low = (header[6] >> 4); + uint8_t mapper_high = (header[7] & 0xF0); + uint8_t mapper_ext = header[8] & 0x0F; + uint8_t submapper = header[8] >> 4; + + uint32_t mapper = (submapper << 12) | (mapper_ext << 8) | mapper_high | mapper_low; + printf("0x%04x %s\n", mapper, path); +} + +static void process_zip_file(const char *path) { + unzFile zip = unzOpen(path); + if(!zip) return; + + if(unzGoToFirstFile(zip) != UNZ_OK) { + unzClose(zip); + return; + } + + char filename[256]; + unz_file_info info; + if(unzGetCurrentFileInfo(zip, &info, filename, sizeof(filename), 0, 0, 0, 0) != UNZ_OK) { + unzClose(zip); + return; + } + + if(strstr(filename, ".nes") == 0) { + unzClose(zip); + return; + } + + if(unzOpenCurrentFile(zip) != UNZ_OK) { + unzClose(zip); + return; + } + + uint8_t header[INES_HEADER_SIZE]; + if(unzReadCurrentFile(zip, header, INES_HEADER_SIZE) != INES_HEADER_SIZE) { + unzCloseCurrentFile(zip); + unzClose(zip); + return; + } + unzCloseCurrentFile(zip); + unzClose(zip); + + if(memcmp(header, "NES\x1A", 4) != 0) return; + + uint8_t mapper_low = (header[6] >> 4); + uint8_t mapper_high = (header[7] & 0xF0); + uint8_t mapper_ext = header[8] & 0x0F; + uint8_t submapper = header[8] >> 4; + + uint32_t mapper = (submapper << 12) | (mapper_ext << 8) | mapper_high | mapper_low; + printf("0x%04x %s\n", mapper, path); +} + +static void scan_directory(const char *path) { + DIR *dir = opendir(path); + if(!dir) return; + + struct dirent *entry; + while((entry = readdir(dir))) { + if(entry->d_name[0] == '.') continue; + + char fullpath[1024]; + snprintf(fullpath, sizeof(fullpath), "%s/%s", path, entry->d_name); + + struct stat st; + if(stat(fullpath, &st) < 0) continue; + + if(S_ISDIR(st.st_mode)) { + scan_directory(fullpath); + } else if(strstr(fullpath, ".nes")) { + process_nes_file(fullpath); + } else if(strstr(fullpath, ".zip")) { + process_zip_file(fullpath); + } + } + closedir(dir); +} + +int main(int argc, char **argv) { + if(argc < 2) { + fprintf(stderr, "usage: %s \n", argv[0]); + return 1; + } + scan_directory(argv[1]); + return 0; +} diff --git a/mapper.c b/mapper.c index 09d9a62..821224d 100644 --- a/mapper.c +++ b/mapper.c @@ -1,7 +1,11 @@ #include "mapper_0000.c" +#include "mapper_0003.c" +#include "mapper_0007.c" +#include "mapper_000b.c" #include "mapper_0042.c" +#include "mapper_2002.c" static uint8_t mapper_default_ciram_read(struct nes_state *state, uint32_t addr) { @@ -32,23 +36,29 @@ static void mapper_default_tick(struct nes_state *state) { // No IRQ or timing l * NOTE(peter): Mapper 0 always has to be first! */ static struct mapper_entry mapper_table[] = { -/* Mapper: 0 */ { 0x00, mapper_0000_prg_read, mapper_0000_prg_write, mapper_0000_chr_read, mapper_0000_chr_write, mapper_default_ciram_read, mapper_default_ciram_write, mapper_default_tick, mapper_0000_init }, -/* Mapper: 66 */ { 0x42, mapper_0042_prg_read, mapper_0042_prg_write, mapper_0042_chr_read, mapper_0042_chr_write, mapper_default_ciram_read, mapper_default_ciram_write, mapper_default_tick, mapper_0042_init }, +/* Mapper: 0 */ { 0x00, mapper_0000_prg_read, mapper_0000_prg_write, mapper_0000_chr_read, mapper_0000_chr_write, mapper_default_ciram_read, mapper_default_ciram_write, mapper_default_tick, mapper_0000_init }, +/* Mapper: 3 */ { 0x03, mapper_0003_prg_read, mapper_0003_prg_write, mapper_0003_chr_read, mapper_0003_chr_write, mapper_default_ciram_read, mapper_default_ciram_write, mapper_default_tick, mapper_0003_init }, +/* Mapper: 7 */ { 0x07, mapper_0007_prg_read, mapper_0007_prg_write, mapper_0007_chr_read, mapper_0007_chr_write, mapper_0007_ciram_read, mapper_0007_ciram_write, mapper_default_tick, mapper_0007_init }, +/* Mapper: b */ { 0x0b, mapper_000b_prg_read, mapper_000b_prg_write, mapper_000b_chr_read, mapper_000b_chr_write, mapper_default_ciram_read, mapper_default_ciram_write, mapper_default_tick, mapper_000b_init }, +/* Mapper: 66 */ { 0x42, mapper_0042_prg_read, mapper_0042_prg_write, mapper_0042_chr_read, mapper_0042_chr_write, mapper_default_ciram_read, mapper_default_ciram_write, mapper_default_tick, mapper_0042_init }, +/* Mapper: 2002 */ { 0x2002, mapper_2002_prg_read, mapper_2002_prg_write, mapper_2002_chr_read, mapper_2002_chr_write, mapper_default_ciram_read, mapper_default_ciram_write, mapper_default_tick, mapper_2002_init }, }; - static void mapper_setup(struct nes_state *state) { - uint32_t mapper = state->ines.mapper; + uint32_t mapper = (state->ines.submapper << 12) | state->ines.mapper; for(uint32_t i = 0; i < sizeof(mapper_table)/sizeof(mapper_table[0]); i++) { if(mapper_table[i].id == mapper) { - state->mapper = mapper_table[i]; + printf("Mapper %4.4x\n", mapper); + memcpy(&state->mapper, &mapper_table[i], sizeof(struct mapper_entry)); // NOTE(peter): BECAUSE GCC IS BROKEN + // state->mapper = mapper_table[i]; state->mapper.init(state); return; } } - printf("Unsupported mapper %d, falling back to NROM\n", mapper); - state->mapper = mapper_table[0]; + printf("Unsupported mapper %4.4x, falling back to NROM\n", mapper); + memcpy(&state->mapper, &mapper_table[0], sizeof(struct mapper_entry)); // NOTE(peter): BECAUSE GCC IS BROKEN + // state->mapper = mapper_table[0]; state->mapper.init(state); } diff --git a/mapper.h b/mapper.h index e7e1437..808bc4e 100644 --- a/mapper.h +++ b/mapper.h @@ -1,9 +1,14 @@ +#include "mapper_0003.h" +#include "mapper_0007.h" +#include "mapper_000b.h" #include "mapper_0042.h" +#include "mapper_2002.h" +struct nes_state; struct mapper_entry { - int id; + uint64_t id; uint8_t (*prg_read)(struct nes_state *state, uint32_t addr); void (*prg_write)(struct nes_state *state, uint32_t addr, uint8_t value); uint8_t (*chr_read)(struct nes_state *state, uint32_t addr); @@ -15,6 +20,10 @@ struct mapper_entry { }; union mapper_data { + struct mapper_0003 m0003; + struct mapper_0007 m0007; + struct mapper_000b m000b; struct mapper_0042 m0042; + struct mapper_2002 m2002; }; diff --git a/mapper_0003.c b/mapper_0003.c new file mode 100644 index 0000000..743df51 --- /dev/null +++ b/mapper_0003.c @@ -0,0 +1,25 @@ + + +static void mapper_0003_init(struct nes_state *state) { + state->map.m0003.chr_ptr = state->chr_rom; +} + +static uint8_t mapper_0003_prg_read(struct nes_state *state, uint32_t addr) { + if(addr >= 0x8000) { + return state->prg_rom[addr - 0x8000]; + } + return 0; +} + +static void mapper_0003_prg_write(struct nes_state *state, uint32_t addr, uint8_t value) { + if(addr >= 0x8000) { + state->map.m0003.chr_ptr = state->chr_rom + (value & 3) * 0x2000; + } +} + +static uint8_t mapper_0003_chr_read(struct nes_state *state, uint32_t addr) { + return state->map.m0003.chr_ptr[addr]; +} + +static void mapper_0003_chr_write(struct nes_state *state, uint32_t addr, uint8_t value) { +} diff --git a/mapper_0003.h b/mapper_0003.h new file mode 100644 index 0000000..c562519 --- /dev/null +++ b/mapper_0003.h @@ -0,0 +1,4 @@ + +struct mapper_0003 { + uint8_t *chr_ptr; +}; diff --git a/mapper_0007.c b/mapper_0007.c new file mode 100644 index 0000000..c1ad7fa --- /dev/null +++ b/mapper_0007.c @@ -0,0 +1,44 @@ +static void mapper_0007_init(struct nes_state *state) { + state->map.m0007.prg_ptr = state->prg_rom; + state->map.m0007.mirroring = 0; +} + +static uint8_t mapper_0007_prg_read(struct nes_state *state, uint32_t addr) { + if(addr >= 0x8000) { + return state->map.m0007.prg_ptr[addr - 0x8000]; + } + return 0; +} + +static void mapper_0007_prg_write(struct nes_state *state, uint32_t addr, uint8_t value) { + if(addr >= 0x8000) { + state->map.m0007.prg_ptr = state->prg_rom + ((value & 0x07) * 0x8000); + state->map.m0007.mirroring = (value & 0x10) ? 1 : 0; + } +} + +static uint8_t mapper_0007_chr_read(struct nes_state *state, uint32_t addr) { + return state->chr_ram[addr]; +} + +static void mapper_0007_chr_write(struct nes_state *state, uint32_t addr, uint8_t value) { + state->chr_ram[addr] = value; +} + +static uint8_t mapper_0007_ciram_read(struct nes_state *state, uint32_t addr) { + if(state->map.m0007.mirroring == 0) { + addr = (addr & 0x800) | (addr & 0x3ff); + } else { + addr = addr & 0x7ff; + } + return state->ciram[addr]; +} + +static void mapper_0007_ciram_write(struct nes_state *state, uint32_t addr, uint8_t value) { + if(state->map.m0007.mirroring == 0) { + addr = (addr & 0x800) | (addr & 0x3ff); + } else { + addr = addr & 0x7ff; + } + state->ciram[addr] = value; +} diff --git a/mapper_0007.h b/mapper_0007.h new file mode 100644 index 0000000..632b57c --- /dev/null +++ b/mapper_0007.h @@ -0,0 +1,7 @@ + + +struct mapper_0007 { + uint8_t *prg_ptr; + uint8_t mirroring; +}; + diff --git a/mapper_000b.c b/mapper_000b.c new file mode 100644 index 0000000..9252060 --- /dev/null +++ b/mapper_000b.c @@ -0,0 +1,27 @@ + + +static void mapper_000b_init(struct nes_state *state) { + state->map.m000b.prg_ptr = state->prg_rom; + state->map.m000b.chr_ptr = state->chr_rom; +} + +static uint8_t mapper_000b_prg_read(struct nes_state *state, uint32_t addr) { + if(addr >= 0x8000) { + return state->map.m000b.prg_ptr[addr - 0x8000]; + } + return 0; +} + +static void mapper_000b_prg_write(struct nes_state *state, uint32_t addr, uint8_t value) { + if(addr >= 0x8000) { + state->map.m000b.prg_ptr = state->prg_rom + ((value >> 4) & 7) * 0x8000; + state->map.m000b.chr_ptr = state->chr_rom + (value & 0x0F) * 0x2000; + } +} + +static uint8_t mapper_000b_chr_read(struct nes_state *state, uint32_t addr) { + return state->map.m000b.chr_ptr[addr]; +} + +static void mapper_000b_chr_write(struct nes_state *state, uint32_t addr, uint8_t value) { +} diff --git a/mapper_000b.h b/mapper_000b.h new file mode 100644 index 0000000..2c3f15a --- /dev/null +++ b/mapper_000b.h @@ -0,0 +1,6 @@ + + +struct mapper_000b { + uint8_t *prg_ptr; + uint8_t *chr_ptr; +}; diff --git a/mapper_2002.c b/mapper_2002.c new file mode 100644 index 0000000..700a24b --- /dev/null +++ b/mapper_2002.c @@ -0,0 +1,28 @@ + +static void mapper_2002_init(struct nes_state *state) { + state->map.m2002.prg_bank0 = state->prg_rom; // default to bank 0 + state->map.m2002.prg_bank1 = state->prg_rom + state->ines.prg_size - 0x4000; +} + +static uint8_t mapper_2002_prg_read(struct nes_state *state, uint32_t addr) { + if(addr >= 0x8000 && addr < 0xc000) { + return state->map.m2002.prg_bank0[addr & 0x3fff]; + } else if(addr >= 0xc000) { + return state->map.m2002.prg_bank1[addr & 0x3fff]; + } + return 0; +} + +static void mapper_2002_prg_write(struct nes_state *state, uint32_t addr, uint8_t value) { + if(addr >= 0x8000) { + state->map.m2002.prg_bank0 = state->prg_rom + ((value & 0x0f) * 0x4000); + } +} + +static uint8_t mapper_2002_chr_read(struct nes_state *state, uint32_t addr) { + return state->chr_ram[addr]; +} + +static void mapper_2002_chr_write(struct nes_state *state, uint32_t addr, uint8_t value) { + state->chr_ram[addr] = value; +} diff --git a/mapper_2002.h b/mapper_2002.h new file mode 100644 index 0000000..6c648b5 --- /dev/null +++ b/mapper_2002.h @@ -0,0 +1,7 @@ + + + +struct mapper_2002 { + uint8_t *prg_bank0; // $8000–BFFF (switchable) + uint8_t *prg_bank1; // $C000–FFFF (fixed to last 16KB) +}; diff --git a/memory.c b/memory.c index 28c51ad..a2e094f 100644 --- a/memory.c +++ b/memory.c @@ -10,9 +10,11 @@ static uint8_t memory_read(struct nes_state *restrict state, uint32_t offset) { return state->ram[offset & 0x07ff]; } else if(offset < 0x4000) { return ppu_read(state, offset); - // } else if(offset < 0x4020) { - // // TODO: APU and I/O reads - // return 0; + } else if(offset == 0x4016 || offset == 0x4017) { + uint32_t index = offset & 1; + uint8_t value = (state->input_latch[index] >> state->input_bit[index]) & 1; + state->input_bit[index]++; + return value | 0x40; // Bit 6 open bus high, bit 7 low } else if(LIKELY(offset >= 0x6000)) { return state->mapper.prg_read(state, offset); } @@ -30,8 +32,17 @@ static void memory_write(struct nes_state *restrict state, uint32_t offset, uint ppu_write(state, offset, value); } else if(offset == 0x4014) { ppu_dma_4014(state, value); - // } else if(offset < 0x4020) { - // // TODO: APU and I/O writes + } else if(offset == 0x4016) { + uint8_t prev = state->input_strobe; + state->input_strobe = value & 1; + + if(prev == 1 && (value & 1) == 0) { + // Latch current inputs + state->input_latch[0] = state->input[0]; + state->input_latch[1] = state->input[1]; + state->input_bit[0] = 0; + state->input_bit[1] = 0; + } } else if(offset >= 0x6000) { state->mapper.prg_write(state, offset, value); } diff --git a/mknes.c b/mknes.c index 25e920f..7417bed 100644 --- a/mknes.c +++ b/mknes.c @@ -67,10 +67,7 @@ struct main_state state; uint32_t buffer[BUFFER_WIDTH * BUFFER_HEIGHT] __attribute__((section(".bss"), aligned(4096))); uint32_t display_buffer[BUFFER_WIDTH * BUFFER_HEIGHT] __attribute__((section(".bss"), aligned(4096))); -void audio_callback(int16_t *data, size_t frames) { - -} - +static void audio_callback(int16_t *data, size_t frames) { } #define FRAME_INTERVAL_NS (1000000000ULL / 60.0988) @@ -84,7 +81,6 @@ static GLFWwindow *window; #include "opengl.c" #include "render.c" -#include "callbacks.c" // NES core #include "mapper.h" @@ -96,6 +92,7 @@ static GLFWwindow *window; #include "ines2.c" #include "mapper.c" +#include "callbacks.c" struct nes_state nstate; static uint32_t frames; @@ -103,40 +100,29 @@ static uint32_t frames; #define PRG_ROM_SIZE (512 * 1024) #define CHR_ROM_SIZE (512 * 1024) -#define PIXELS_SIZE (256 * 240) +#define PIXELS_SIZE (256 * 240) #define RAM_SIZE 0x800 #define SRAM_SIZE 0x2000 -#define CIRAM_SIZE 0x1000 +#define CIRAM_SIZE 0x1000 +#define CHR_RAM_SIZE 0x4000 static struct nes_state *allocate_nes_state(void) { struct nes_state *state = (struct nes_state*)calloc(1, sizeof(struct nes_state)); if(!state) return 0; - size_t total_size = (PRG_ROM_SIZE + CHR_ROM_SIZE + PIXELS_SIZE + RAM_SIZE + SRAM_SIZE + CIRAM_SIZE + 4095) & ~0xfff; + size_t total_size = (PRG_ROM_SIZE + CHR_ROM_SIZE + PIXELS_SIZE + RAM_SIZE + SRAM_SIZE + CIRAM_SIZE + CHR_RAM_SIZE+ 4095) & ~0xfff; uint8_t *m = (uint8_t*)aligned_alloc(4096, total_size); memset(m, 0, total_size); size_t offset = 0; - - state->prg_rom = m + offset; - offset += PRG_ROM_SIZE; - - state->chr_rom = m + offset; - offset += CHR_ROM_SIZE; - - state->pixels = m + offset; - offset += PIXELS_SIZE; - - state->ram = m + offset; - offset += RAM_SIZE; - - state->sram = m + offset; - offset += SRAM_SIZE; - - state->ciram = m + offset; - offset += CIRAM_SIZE; - + state->prg_rom = m + offset; offset += PRG_ROM_SIZE; + state->chr_rom = m + offset; offset += CHR_ROM_SIZE; + state->pixels = m + offset; offset += PIXELS_SIZE; + state->ram = m + offset; offset += RAM_SIZE; + state->sram = m + offset; offset += SRAM_SIZE; + state->ciram = m + offset; offset += CIRAM_SIZE; + state->chr_ram = m + offset; offset += CHR_RAM_SIZE; return state; } @@ -154,29 +140,46 @@ int main(int argc, char **argv) { init_opcode_ud_lut(); // protect_opcode_lut(); ppu_reset(nstate); - // ines2_load(nstate, "data/nrom/10-Yard Fight (USA, Europe).nes"); - // ines2_load(nstate, "data/nrom/Balloon Fight (USA).nes"); - // ines2_load(nstate, "data/nrom/Excitebike (Japan, USA).nes"); - // ines2_load(nstate, "data/nrom/Ice Climber (USA, Europe, Korea).nes"); - // ines2_load(nstate, "data/nrom/Kung Fu (Japan, USA).nes"); - ines2_load(nstate, "data/nrom/Super Mario Bros. (World) (HVC-SM).nes"); - // ines2_load(nstate, "data/nrom/Urban Champion (World).nes"); - // ines2_load(nstate, "data/nrom/Wrecking Crew (World).nes"); - // ines2_load(nstate, "data/nrom/scanline.nes"); - // ines2_load(nstate, "data/nrom/Sayoonara!.NES"); - // ines2_load(nstate, "data/nrom/raster_demos/RasterChromaLuma.NES"); - // ines2_load(nstate, "data/nrom/raster_demos/RasterTest1.NES"); - // ines2_load(nstate, "data/nrom/raster_demos/RasterTest2.NES"); - // ines2_load(nstate, "data/nrom/raster_demos/RasterTest3.NES"); - // ines2_load(nstate, "data/nrom/raster_demos/RasterTest3a.NES"); - // ines2_load(nstate, "data/nrom/raster_demos/RasterTest3b.NES"); - // ines2_load(nstate, "data/nrom/raster_demos/RasterTest3c.NES"); - // ines2_load(nstate, "data/nrom/raster_demos/RasterTest3d.NES"); - // ines2_load(nstate, "data/nrom/raster_demos/RasterTest3e.NES"); - // ines2_load(nstate, "data/nrom/NEStress.NES"); + // ines2_load(nstate, "data/0000/10-Yard Fight (USA, Europe).nes"); + // ines2_load(nstate, "data/0000/Balloon Fight (USA).nes"); + // ines2_load(nstate, "data/0000/Excitebike (Japan, USA).nes"); + // ines2_load(nstate, "data/0000/Ice Climber (USA, Europe, Korea).nes"); + // ines2_load(nstate, "data/0000/Kung Fu (Japan, USA).nes"); + ines2_load(nstate, "data/0000/Super Mario Bros. (World) (HVC-SM).nes"); + // ines2_load(nstate, "data/0000/Urban Champion (World).nes"); + // ines2_load(nstate, "data/0000/Wrecking Crew (World).nes"); + // ines2_load(nstate, "data/0000/scanline.nes"); + // ines2_load(nstate, "data/0000/Sayoonara!.NES"); + // ines2_load(nstate, "data/0000/raster_demos/RasterChromaLuma.NES"); + // ines2_load(nstate, "data/0000/raster_demos/RasterTest1.NES"); + // ines2_load(nstate, "data/0000/raster_demos/RasterTest2.NES"); + // ines2_load(nstate, "data/0000/raster_demos/RasterTest3.NES"); + // ines2_load(nstate, "data/0000/raster_demos/RasterTest3a.NES"); + // ines2_load(nstate, "data/0000/raster_demos/RasterTest3b.NES"); + // ines2_load(nstate, "data/0000/raster_demos/RasterTest3c.NES"); + // ines2_load(nstate, "data/0000/raster_demos/RasterTest3d.NES"); + // ines2_load(nstate, "data/0000/raster_demos/RasterTest3e.NES"); + // ines2_load(nstate, "data/0000/NEStress.NES"); + // ines2_load(nstate, "data/0000/Super Mario Bros. (World) (HVC-SM).zip"); + // ines2_load(nstate, "data/0042/Super Mario Bros. + Duck Hunt (USA).zip"); + // ines2_load(nstate, "data/0000/Xevious - The Avenger (USA).zip"); // ines2_load(nstate, "data/tv.nes"); - // ines2_load(nstate, "data/Super Mario Bros. (World) (HVC-SM).zip"); - // ines2_load(nstate, "data/Super Mario Bros. + Duck Hunt (USA).zip"); + + // ines2_load(nstate, "data/0003/Flipull - An Exciting Cube Game (Japan) (En).zip"); + // ines2_load(nstate, "data/0003/Friday the 13th (USA).zip"); + // ines2_load(nstate, "data/0003/Ghostbusters (Japan).zip"); + + // ines2_load(nstate, "data/0007/Battletoads (USA).zip"); + // ines2_load(nstate, "data/0007/Beetlejuice (USA).zip"); + // ines2_load(nstate, "data/0007/Cabal (USA).zip"); + + // ines2_load(nstate, "data/000b/Baby Boomer (USA) (Unl).zip"); + // ines2_load(nstate, "data/000b/Captain Comic - The Adventure (USA) (Unl).zip"); + // ines2_load(nstate, "data/000b/King Neptune's Adventure (USA) (Unl).zip"); + + // ines2_load(nstate, "data/2002/Attack Animal Gakuen (Japan).zip"); + // ines2_load(nstate, "data/2002/Ballblazer (Japan).zip"); + // ines2_load(nstate, "data/2002/Best of the Best - Championship Karate (USA).zip"); mapper_setup(nstate); uint32_t lo = nstate->mapper.prg_read(nstate, 0xfffc); @@ -197,6 +200,7 @@ int main(int argc, char **argv) { window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "NES Emulator", 0, 0); if(window) { + glfwSetWindowUserPointer(window, (void*)nstate); glfwSetWindowAspectRatio(window, 320, 240); glfwSetWindowSizeLimits(window, 320*3, 240*3, GLFW_DONT_CARE, GLFW_DONT_CARE); @@ -208,6 +212,7 @@ int main(int argc, char **argv) { framebuffer_callback(window, WINDOW_WIDTH, WINDOW_HEIGHT); + for(int jid = GLFW_JOYSTICK_1; jid <= GLFW_JOYSTICK_LAST; jid++) { if(glfwJoystickPresent(jid)) { const char *name = glfwGetJoystickName(jid); @@ -216,14 +221,14 @@ int main(int argc, char **argv) { } } - set_decay(40); + set_decay(10); timer_start(timer); - while(!glfwWindowShouldClose(window)) { - // for(uint32_t i = 0; i < 0x5000; ++ i) { - timer_wait(timer); - glfwPollEvents(); + // while(!glfwWindowShouldClose(window)) { + for(uint32_t i = 0; i < 0x5000; ++ i) { + // timer_wait(timer); + // glfwPollEvents(); // // while(!nstate->ppu.frame_ready) { @@ -233,19 +238,19 @@ int main(int argc, char **argv) { nstate->ppu.frame_ready = 0; frames++; - uint32_t * restrict dst = buffer; - uint8_t * restrict src = nstate->pixels; - for(uint32_t y = 0; y < 240; ++y) { - for(uint32_t x = 0; x < 256; ++x) { - uint8_t val = *src++; - if(val >= 64) val = 0; - dst[x] = nes_palette[val]; - } - dst += BUFFER_WIDTH; - } - apply_phosphor_decay(); - render_frame(); - glfwSwapBuffers(window); + // uint32_t * restrict dst = buffer; + // uint8_t * restrict src = nstate->pixels; + // for(uint32_t y = 0; y < 240; ++y) { + // for(uint32_t x = 0; x < 256; ++x) { + // uint8_t val = *src++; + // if(val >= 64) val = 0; + // dst[x] = nes_palette[val]; + // } + // dst += BUFFER_WIDTH; + // } + // apply_phosphor_decay(); + // render_frame(); + // glfwSwapBuffers(window); } printf("total frames: %6.6d total cycles: %ld\n", frames, nstate->cycles); glfwDestroyWindow(window); diff --git a/mknes.h b/mknes.h index c1784f6..a296fe4 100644 --- a/mknes.h +++ b/mknes.h @@ -97,15 +97,22 @@ struct nes_state { struct cpu_state cpu; uint8_t irq_pending; uint8_t nmi_pending; + uint8_t input[2]; // Controller 1 & 2 + uint8_t input_latch[2]; // Latched inputs after strobe + uint8_t input_strobe; // Control bit (0 or 1) + uint8_t input_bit[2]; // Current bit position being shifted out struct ppu_state ppu; + struct mapper_entry mapper; union mapper_data map; + uint8_t *pixels; uint8_t *ram; uint8_t *sram; uint8_t *ciram; uint8_t *prg_rom; uint8_t *chr_rom; + uint8_t *chr_ram; }; diff --git a/ppu.c b/ppu.c index db78b91..da360e0 100644 --- a/ppu.c +++ b/ppu.c @@ -124,8 +124,8 @@ static inline uint8_t ppu_read(struct nes_state *state, uint32_t offset) { return result; } -__attribute__((hot)) -static void ppu_evaluate_sprites(struct nes_state *state) { +__attribute__((always_inline, hot)) +static inline void ppu_evaluate_sprites(struct nes_state *state) { struct ppu_state *ppu = &state->ppu; uint8_t sprite_height = (ppu->reg_ctrl & 0x20) ? 16 : 8; uint8_t n = 0; @@ -158,8 +158,8 @@ static void ppu_evaluate_sprites(struct nes_state *state) { ppu->sprite_count = n; } -__attribute__((hot)) -static void ppu_fetch_sprite_patterns(struct nes_state *state) { +__attribute__((always_inline, hot)) +static inline void ppu_fetch_sprite_patterns(struct nes_state *state) { struct ppu_state *ppu = &state->ppu; uint32_t addr; uint32_t bank; @@ -201,16 +201,14 @@ static void ppu_fetch_sprite_patterns(struct nes_state *state) { } } -__attribute__((hot)) -static void ppu_render_pixel(struct nes_state *state) { +__attribute__((always_inline, hot)) +static inline void ppu_render_pixel(struct nes_state *state) { struct ppu_state *ppu = &state->ppu; uint32_t x = ppu->dot - 1; uint32_t y = ppu->scanline; - // Fine X shift mask - // static const uint16_t fine_shift[8] = { 0x8000, 0x4000, 0x2000, 0x1000, 0x0800, 0x0400, 0x0200, 0x0100 }; - uint16_t bit = 0x8000 >> ppu->fine_x;//fine_shift[ppu->fine_x]; + uint16_t bit = 0x8000 >> ppu->fine_x; uint8_t bg_pixel = 0; uint8_t bg_palette = 0; @@ -219,7 +217,6 @@ static void ppu_render_pixel(struct nes_state *state) { uint8_t sp_prio = 0; uint8_t sp_zero = 0; -#if 1 // TODO(peter): Decide what I prefer, masking away unlikely path, or LIKELY hint to the compiler uint8_t bg_mask = (ppu->reg_mask & 0x08) ? 0xff : 0x00; uint8_t sp_mask = (ppu->reg_mask & 0x10) ? 0xff : 0x00; @@ -247,36 +244,7 @@ static void ppu_render_pixel(struct nes_state *state) { sp_zero = (ppu->sprite_indexes[i] == 0); break; } -#else - // Background fetch - if(LIKELY(ppu->reg_mask & 0x08)) { - uint8_t p0 = !!(ppu->bg_shift_pattern_low & bit); - uint8_t p1 = !!(ppu->bg_shift_pattern_high & bit); - bg_pixel = (p1 << 1) | p0; - - uint8_t a0 = !!(ppu->bg_shift_attrib_low & bit); - uint8_t a1 = !!(ppu->bg_shift_attrib_high & bit); - bg_palette = (a1 << 1) | a0; - } - - // Sprite fetch - if(LIKELY(ppu->reg_mask & 0x10)) { - for(uint8_t i = 0; i < ppu->sprite_count; i++) { - if(ppu->sprite_positions[i]) continue; - - uint8_t lo = ppu->sprite_shift_lo[i]; - uint8_t hi = ppu->sprite_shift_hi[i]; - sp_pixel = ((hi & 0x80) >> 6) | ((lo & 0x80) >> 7); - - if(!sp_pixel) continue; - sp_palette = ppu->secondary_oam[i * 4 + 2] & 3; - sp_prio = ppu->sprite_priorities[i]; - sp_zero = (ppu->sprite_indexes[i] == 0); - break; - } - } -#endif // Final pixel composition uint8_t palette_index = 0; uint8_t bg_index = (bg_palette << 2) + bg_pixel; @@ -309,117 +277,222 @@ static void ppu_tick(struct nes_state *state) { for(uint32_t ppu_loops = 0; ppu_loops < 3; ++ppu_loops) { if(LIKELY(rendering)) { + switch(scanline) { + case 0 ... 239: { + switch(dot) { + case 1: + __attribute__((fallthrough)); + + case 2 ... 256: // fallthrough: this is 1->256 + ppu_render_pixel(state); + + if(UNLIKELY(dot == 256)) { + if((ppu->vram_addr & 0x7000) != 0x7000) { + ppu->vram_addr += 0x1000; + } else { + ppu->vram_addr &= ~0x7000; + uint32_t y = (ppu->vram_addr & 0x03e0) >> 5; + if(y == 29) { + y = 0; + ppu->vram_addr ^= 0x0800; + } else if(y == 31) { + y = 0; + } else { + y++; + } + ppu->vram_addr = (ppu->vram_addr & ~0x03e0) | (y << 5); + } + } + __attribute__((fallthrough)); + + case 321 ... 336: { // fallthrough: the code below has to run 1->256 + 321->336 + // Rendering and tile fetch goes here + if(ppu->reg_mask & 0x10) { + for(uint32_t i = 0; i < ppu->sprite_count; i++) { + if(ppu->sprite_positions[i] > 0) { + ppu->sprite_positions[i]--; + } else { + ppu->sprite_shift_lo[i] <<= 1; + ppu->sprite_shift_hi[i] <<= 1; + } + } + } - if(ppu->even_frame && dot == 0) { - // call mapper_tick here. - ppu->dot++; - } + ppu->bg_shift_pattern_low <<= 1; + ppu->bg_shift_pattern_high <<= 1; + ppu->bg_shift_attrib_low <<= 1; + ppu->bg_shift_attrib_high <<= 1; + + switch(dot % 8) { + case 1: { + uint32_t nt_addr = 0x2000 | (ppu->vram_addr & 0x0fff); + ppu->bg_next_tile_id = state->mapper.ciram_read(state, nt_addr); + break; + } + case 3: { + uint32_t attr_addr = 0x23c0 | (ppu->vram_addr & 0x0c00) | ((ppu->vram_addr >> 4) & 0x38) | ((ppu->vram_addr >> 2) & 0x07); + uint8_t attr = state->mapper.ciram_read(state, attr_addr & 0x0fff); + uint8_t shift = ((ppu->vram_addr >> 4) & 4) | (ppu->vram_addr & 2); + ppu->bg_next_tile_attrib = (attr >> shift) & 3; + break; + } + case 5: { + uint32_t base = (ppu->reg_ctrl & 0x10) ? 0x1000 : 0x0000; + uint32_t tile = ppu->bg_next_tile_id; + uint32_t fine_y = (ppu->vram_addr >> 12) & 7; + uint32_t addr_lsb = (base + tile * 16 + fine_y) & 0x1fff; + ppu->bg_next_tile_lsb = state->mapper.chr_read(state, addr_lsb); + break; + } + case 7: { + uint32_t base = (ppu->reg_ctrl & 0x10) ? 0x1000 : 0x0000; + uint32_t tile = ppu->bg_next_tile_id; + uint32_t fine_y = (ppu->vram_addr >> 12) & 7; + uint32_t addr_msb = (base + tile * 16 + fine_y + 8) & 0x1fff; + ppu->bg_next_tile_msb = state->mapper.chr_read(state, addr_msb); + break; + } + case 0: { + ppu->bg_shift_pattern_low = (ppu->bg_shift_pattern_low & 0xff00) | ppu->bg_next_tile_lsb; + ppu->bg_shift_pattern_high = (ppu->bg_shift_pattern_high & 0xff00) | ppu->bg_next_tile_msb; + + uint8_t a = ppu->bg_next_tile_attrib; + ppu->bg_shift_attrib_low = (ppu->bg_shift_attrib_low & 0xff00) | ((a & 1) ? 0xff : 0x00); + ppu->bg_shift_attrib_high = (ppu->bg_shift_attrib_high & 0xff00) | ((a & 2) ? 0xff : 0x00); + + if((ppu->vram_addr & 0x001f) == 31) { + ppu->vram_addr &= ~0x001f; + ppu->vram_addr ^= 0x0400; + } else { + ppu->vram_addr++; + } + + break; + } + } + } break; - if(scanline < 240 && dot >= 1 && dot <= 256) { - ppu_render_pixel(state); - } + case 257: { // Sprite evaluation and horizontal vram transfer + ppu->vram_addr = (ppu->vram_addr & ~0x041f) | (ppu->temp_addr & 0x041f); + ppu_evaluate_sprites(state); + } break; - if(scanline < 240 || scanline == 261) { - if((dot >= 1 && dot <= 256) || (dot >= 321 && dot <= 336)) { + case 340: { // sprite fetch pattern + ppu_fetch_sprite_patterns(state); + } break; + } - if(ppu->reg_mask & 0x10) { - for(uint32_t i = 0; i < ppu->sprite_count; i++) { - if(ppu->sprite_positions[i] > 0) { - ppu->sprite_positions[i]--; - } else { - ppu->sprite_shift_lo[i] <<= 1; - ppu->sprite_shift_hi[i] <<= 1; + } break; + + case 261: { + switch(dot) { + case 1: + // Clear vblank, sprite 0 hit, etc. + __attribute__((fallthrough)); + + case 2 ... 256: + if(UNLIKELY(dot == 256)) { + if((ppu->vram_addr & 0x7000) != 0x7000) { + ppu->vram_addr += 0x1000; + } else { + ppu->vram_addr &= ~0x7000; + uint32_t y = (ppu->vram_addr & 0x03e0) >> 5; + if(y == 29) { + y = 0; + ppu->vram_addr ^= 0x0800; + } else if(y == 31) { + y = 0; + } else { + y++; + } + ppu->vram_addr = (ppu->vram_addr & ~0x03e0) | (y << 5); + } } - } - } - ppu->bg_shift_pattern_low <<= 1; - ppu->bg_shift_pattern_high <<= 1; - ppu->bg_shift_attrib_low <<= 1; - ppu->bg_shift_attrib_high <<= 1; - - switch(dot % 8) { - case 1: { - uint32_t nt_addr = 0x2000 | (ppu->vram_addr & 0x0fff); - ppu->bg_next_tile_id = state->mapper.ciram_read(state, nt_addr); - break; - } - case 3: { - uint32_t attr_addr = 0x23c0 | (ppu->vram_addr & 0x0c00) | ((ppu->vram_addr >> 4) & 0x38) | ((ppu->vram_addr >> 2) & 0x07); - uint8_t attr = state->mapper.ciram_read(state, attr_addr & 0x0fff); - uint8_t shift = ((ppu->vram_addr >> 4) & 4) | (ppu->vram_addr & 2); - ppu->bg_next_tile_attrib = (attr >> shift) & 3; - break; - } - case 5: { - uint32_t base = (ppu->reg_ctrl & 0x10) ? 0x1000 : 0x0000; - uint32_t tile = ppu->bg_next_tile_id; - uint32_t fine_y = (ppu->vram_addr >> 12) & 7; - uint32_t addr_lsb = (base + tile * 16 + fine_y) & 0x1fff; - ppu->bg_next_tile_lsb = state->mapper.chr_read(state, addr_lsb); - break; - } - case 7: { - uint32_t base = (ppu->reg_ctrl & 0x10) ? 0x1000 : 0x0000; - uint32_t tile = ppu->bg_next_tile_id; - uint32_t fine_y = (ppu->vram_addr >> 12) & 7; - uint32_t addr_msb = (base + tile * 16 + fine_y + 8) & 0x1fff; - ppu->bg_next_tile_msb = state->mapper.chr_read(state, addr_msb); - break; - } - case 0: { - ppu->bg_shift_pattern_low = (ppu->bg_shift_pattern_low & 0xff00) | ppu->bg_next_tile_lsb; - ppu->bg_shift_pattern_high = (ppu->bg_shift_pattern_high & 0xff00) | ppu->bg_next_tile_msb; - - uint8_t a = ppu->bg_next_tile_attrib; - ppu->bg_shift_attrib_low = (ppu->bg_shift_attrib_low & 0xff00) | ((a & 1) ? 0xff : 0x00); - ppu->bg_shift_attrib_high = (ppu->bg_shift_attrib_high & 0xff00) | ((a & 2) ? 0xff : 0x00); - - if((ppu->vram_addr & 0x001f) == 31) { - ppu->vram_addr &= ~0x001f; - ppu->vram_addr ^= 0x0400; - } else { - ppu->vram_addr++; + __attribute__((fallthrough)); + case 321 ... 336: { // Rendering and tile fetch + if(ppu->reg_mask & 0x10) { + for(uint32_t i = 0; i < ppu->sprite_count; i++) { + if(ppu->sprite_positions[i] > 0) { + ppu->sprite_positions[i]--; + } else { + ppu->sprite_shift_lo[i] <<= 1; + ppu->sprite_shift_hi[i] <<= 1; + } + } } - break; - } - } - } + ppu->bg_shift_pattern_low <<= 1; + ppu->bg_shift_pattern_high <<= 1; + ppu->bg_shift_attrib_low <<= 1; + ppu->bg_shift_attrib_high <<= 1; + + switch(dot % 8) { + case 1: { + uint32_t nt_addr = 0x2000 | (ppu->vram_addr & 0x0fff); + ppu->bg_next_tile_id = state->mapper.ciram_read(state, nt_addr); + break; + } + case 3: { + uint32_t attr_addr = 0x23c0 | (ppu->vram_addr & 0x0c00) | ((ppu->vram_addr >> 4) & 0x38) | ((ppu->vram_addr >> 2) & 0x07); + uint8_t attr = state->mapper.ciram_read(state, attr_addr & 0x0fff); + uint8_t shift = ((ppu->vram_addr >> 4) & 4) | (ppu->vram_addr & 2); + ppu->bg_next_tile_attrib = (attr >> shift) & 3; + break; + } + case 5: { + uint32_t base = (ppu->reg_ctrl & 0x10) ? 0x1000 : 0x0000; + uint32_t tile = ppu->bg_next_tile_id; + uint32_t fine_y = (ppu->vram_addr >> 12) & 7; + uint32_t addr_lsb = (base + tile * 16 + fine_y) & 0x1fff; + ppu->bg_next_tile_lsb = state->mapper.chr_read(state, addr_lsb); + break; + } + case 7: { + uint32_t base = (ppu->reg_ctrl & 0x10) ? 0x1000 : 0x0000; + uint32_t tile = ppu->bg_next_tile_id; + uint32_t fine_y = (ppu->vram_addr >> 12) & 7; + uint32_t addr_msb = (base + tile * 16 + fine_y + 8) & 0x1fff; + ppu->bg_next_tile_msb = state->mapper.chr_read(state, addr_msb); + break; + } + case 0: { + ppu->bg_shift_pattern_low = (ppu->bg_shift_pattern_low & 0xff00) | ppu->bg_next_tile_lsb; + ppu->bg_shift_pattern_high = (ppu->bg_shift_pattern_high & 0xff00) | ppu->bg_next_tile_msb; + + uint8_t a = ppu->bg_next_tile_attrib; + ppu->bg_shift_attrib_low = (ppu->bg_shift_attrib_low & 0xff00) | ((a & 1) ? 0xff : 0x00); + ppu->bg_shift_attrib_high = (ppu->bg_shift_attrib_high & 0xff00) | ((a & 2) ? 0xff : 0x00); + + if((ppu->vram_addr & 0x001f) == 31) { + ppu->vram_addr &= ~0x001f; + ppu->vram_addr ^= 0x0400; + } else { + ppu->vram_addr++; + } + + break; + } + } + } break; - if(dot == 256) { - if((ppu->vram_addr & 0x7000) != 0x7000) { - ppu->vram_addr += 0x1000; - } else { - ppu->vram_addr &= ~0x7000; - uint32_t y = (ppu->vram_addr & 0x03e0) >> 5; - if(y == 29) { - y = 0; - ppu->vram_addr ^= 0x0800; - } else if(y == 31) { - y = 0; - } else { - y++; - } - ppu->vram_addr = (ppu->vram_addr & ~0x03e0) | (y << 5); - } - } + case 257: { // Sprite evaluation and horizontal vram transfer + ppu->vram_addr = (ppu->vram_addr & ~0x041f) | (ppu->temp_addr & 0x041f); + ppu_evaluate_sprites(state); - if(dot == 257) { - ppu->vram_addr = (ppu->vram_addr & ~0x041f) | (ppu->temp_addr & 0x041f); - } + } break; - if(UNLIKELY(scanline == 261) && dot >= 280 && dot <= 304) { - ppu->vram_addr = (ppu->vram_addr & ~0x7be0) | (ppu->temp_addr & 0x7be0); - } + case 280 ... 304: { // Vertical vram transfer + ppu->vram_addr = (ppu->vram_addr & ~0x7be0) | (ppu->temp_addr & 0x7be0); + } break; - if(dot == 257 && LIKELY(scanline < 240)) { - ppu_evaluate_sprites(state); - } + case 340: { // Sprite pattern fetch + ppu_fetch_sprite_patterns(state); + } break; - if(dot == 340 && (LIKELY(scanline < 240) || UNLIKELY(scanline == 261))) { - ppu_fetch_sprite_patterns(state); - } + } + + } break; } } -- cgit v1.2.3