diff options
| author | Peter Fors <peter.fors@mindkiller.com> | 2025-04-05 08:58:12 +0200 |
|---|---|---|
| committer | Peter Fors <peter.fors@mindkiller.com> | 2025-04-05 08:58:12 +0200 |
| commit | f1bd6a7d2f4ffe3e5263e0254bcf7522ab381264 (patch) | |
| tree | e75bde292329f337d619f9a997aab9b17c37e38b | |
| parent | 8c82be43720d9e221a9e2541c9ff6151015838bb (diff) | |
transform to switch case for ppu_tick()
| -rwxr-xr-x | addmapper.py | 67 | ||||
| -rwxr-xr-x | build.sh | 4 | ||||
| -rw-r--r-- | cpu.c | 11 | ||||
| -rw-r--r-- | ines2.c | 122 | ||||
| -rw-r--r-- | mapper.c | 72 | ||||
| -rw-r--r-- | mapper.h | 24 | ||||
| -rw-r--r-- | mapper_0000.c | 24 | ||||
| -rw-r--r-- | mapper_001.c | 46 | ||||
| -rw-r--r-- | mapper_0042.c | 30 | ||||
| -rw-r--r-- | mapper_0042.h (renamed from mapper_066.h) | 2 | ||||
| -rw-r--r-- | mapper_066.c | 58 | ||||
| -rw-r--r-- | memory.c | 76 | ||||
| -rw-r--r-- | mknes.c | 36 | ||||
| -rw-r--r-- | mknes.h | 75 | ||||
| -rw-r--r-- | ppu.c | 435 |
15 files changed, 668 insertions, 414 deletions
diff --git a/addmapper.py b/addmapper.py new file mode 100755 index 0000000..8848e87 --- /dev/null +++ b/addmapper.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 +import sys + +def generate_mapper_files(mapper_id, submapper_id=0): + # Format: submapper (1 hex digit) + mapper_id (3 hex digits, zero-padded) + full_id = f"{submapper_id:01x}{int(mapper_id):03x}" + filename = f"mapper_{full_id}" + + # Generate C file content + c_content = f""" + +static void {filename}_init(struct nes_state *state) {{ +}} + +static uint8_t {filename}_prg_read(struct nes_state *state, uint32_t addr) {{ +}} + +static void {filename}_prg_write(struct nes_state *state, uint32_t addr, uint8_t value) {{ +}} + +static uint8_t {filename}_chr_read(struct nes_state *state, uint32_t addr) {{ +}} + +static void {filename}_chr_write(struct nes_state *state, uint32_t addr, uint8_t value) {{ +}} + +static uint8_t {filename}_ciram_read(struct nes_state *state, uint32_t addr) {{ +}} + +static void {filename}_ciram_write(struct nes_state *state, uint32_t addr, uint8_t value) {{ +}} + +static void {filename}_tick(struct nes_state *state) {{ +}} +""" + + # Generate H file content + h_content = f""" + +struct {filename} {{ +}}; +""" + + # Generate table entry + table_entry = f""" {{ 0x{int(mapper_id):x}, {filename}_prg_read, {filename}_prg_write, {filename}_chr_read, {filename}_chr_write, {filename}_ciram_read, {filename}_ciram_write, {filename}_tick, {filename}_init }},""" + + # Write files + with open(f"{filename}.c", "w") as f: + f.write(c_content) + + with open(f"{filename}.h", "w") as f: + f.write(h_content) + + print(f"Generated files: {filename}.c, {filename}.h") + print("\nTable entry (copy-paste this into your mapper table):") + print(table_entry) + +if __name__ == "__main__": + if len(sys.argv) < 2: + print("Usage: python newmapper.py <mapper_id> [submapper_id=0]") + print("Example: python newmapper.py 123 0 # Creates mapper_0123.*") + sys.exit(1) + + mapper_id = sys.argv[1] + submapper_id = int(sys.argv[2]) if len(sys.argv) > 2 else 0 + generate_mapper_files(mapper_id, submapper_id) + @@ -11,7 +11,7 @@ CFLAGS+="-fno-stack-protector -fno-PIE -no-pie -fno-strict-aliasing -ffunction-s CFLAGS+="-fno-exceptions -fno-rtti -fno-use-cxa-atexit -fno-non-call-exceptions " 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 " +CFLAGS+="-Wno-unused-variable -Wno-unused-const-variable -Wno-unused-function -Wno-write-strings -Wno-missing-field-initializers " LDFLAGS="-Wl,--gc-sections " @@ -20,7 +20,7 @@ INCLUDE_PATHS="-Ibase -I.." # Linux-specific includes and libraries LINUX_INCLUDE="-I/usr/include/pipewire-0.3 -I/usr/include/spa-0.2" -LINUX_LIBS="-lpipewire-0.3 -lXi -lX11 -lGL -lm -ldl -pthread -lglfw" +LINUX_LIBS="-lpipewire-0.3 -lXi -lX11 -lGL -lm -ldl -pthread -lglfw -larchive" # Windows-specific includes and libraries # WINDOWS_INCLUDE="" @@ -92,13 +92,8 @@ static void cpu_tick(struct nes_state *state) { uint8_t opcode; - // if(cpu->pc <= 0x90cc || cpu->pc >= 0x90e6) { - // printf("%5.5d %4.4x: ", line++, 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); - // } else { - opcode = memory_read(state, cpu->pc++); - // } + // printf("%5.5d %4.4x: ", line++, 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); - } @@ -1,5 +1,6 @@ - +#include <archive.h> +#include <archive_entry.h> // iNES header fields #define INES_HEADER_SIZE 16 @@ -27,14 +28,89 @@ #define MIRROR_FOUR_SCREEN 2 -static int ines2_load(struct nes_state *state, char *path) { +static uint8_t *ines2_read_entire_file(const char *path, size_t *out_size) { FILE *f = fopen(path, "rb"); - if(!f) { + if(!f) return 0; + + fseek(f, 0, SEEK_END); + size_t size = ftell(f); + fseek(f, 0, SEEK_SET); + + uint8_t *buffer = (uint8_t *)malloc(size); + size_t read_size = fread(buffer, 1, size, f); + fclose(f); + + if(read_size != size) { + free(buffer); + return 0; + } + + *out_size = size; + return buffer; +} + +uint8_t *ines2_unzip_file_to_memory(const char *zip_path, size_t *out_size) { + struct archive *a = archive_read_new(); + struct archive_entry *entry; + uint8_t *buffer = 0; + size_t size = 0; + + archive_read_support_format_zip(a); + + if(archive_read_open_filename(a, zip_path, 10240) != ARCHIVE_OK) { + fprintf(stderr, "libarchive: failed to open zip: %s\n", archive_error_string(a)); + archive_read_free(a); + return 0; + } + + if(archive_read_next_header(a, &entry) != ARCHIVE_OK) { + fprintf(stderr, "libarchive: no valid file found in zip\n"); + archive_read_free(a); + return 0; + } + + const char *name = archive_entry_pathname(entry); + + size = archive_entry_size(entry); + buffer = (uint8_t*)malloc(size); + if(!buffer) { + fprintf(stderr, "libarchive: malloc failed\n"); + archive_read_free(a); + return 0; + } + + ssize_t read_size = archive_read_data(a, buffer, size); + if(read_size != (ssize_t)size) { + fprintf(stderr, "libarchive: failed to read full file\n"); + free(buffer); + buffer = 0; + } + + + archive_read_free(a); + + if(buffer && out_size) { + *out_size = size; + } + return buffer; +} + +static int ines2_load(struct nes_state *state, const char *path) { + uint8_t *data = 0; + size_t size = 0; + + if(strstr(path, ".zip")) { + data = ines2_unzip_file_to_memory(path, &size); + } else { + data = ines2_read_entire_file(path, &size); + } + + if(!data || size < INES_HEADER_SIZE) { return -1; } - uint8_t header[INES_HEADER_SIZE]; - fread(header, 1, INES_HEADER_SIZE, f); + uint8_t *ptr = data; + uint8_t *header = ptr; ptr += INES_HEADER_SIZE; uint8_t prg_lsb = header[INES_PRG_SIZE_LSB]; uint8_t chr_lsb = header[INES_CHR_SIZE_LSB]; @@ -44,21 +120,18 @@ static int ines2_load(struct nes_state *state, char *path) { uint32_t prg_size = (prg_msb << 8 | prg_lsb) * PRG_ROM_UNIT; uint32_t chr_size = (chr_msb << 8 | chr_lsb) * CHR_ROM_UNIT; - // NES 2.0 exponential format (only if lower byte is 0x0f) - if(prg_lsb == 0x0f) { - prg_size = 1 << header[INES_PRG_EXP]; - } - if(chr_lsb == 0x0f) { - chr_size = 1 << header[INES_CHR_EXP]; - } + if(prg_lsb == 0x0f) prg_size = 1 << header[INES_PRG_EXP]; + if(chr_lsb == 0x0f) chr_size = 1 << header[INES_CHR_EXP]; + + uint8_t mapper_low = (header[INES_FLAGS6] >> 4); + uint8_t mapper_high = (header[INES_FLAGS7] & 0xf0); + uint8_t mapper_ext = (header[INES_PRG_CHR_MSB] & 0x0f); + uint8_t mapper_upper = header[8] & 0x0f; + uint8_t submapper = (header[8] >> 4); - // Extract mapper - uint8_t mapper_low = (header[INES_FLAGS6] >> 4); - uint8_t mapper_high = (header[INES_FLAGS7] & 0xf0); - uint8_t mapper_ext = (header[INES_PRG_CHR_MSB] & 0x0f); - state->ines.mapper = mapper_low | mapper_high | (mapper_ext << 8); + state->ines.mapper = mapper_low | mapper_high | (mapper_upper << 8); + state->ines.submapper = submapper; - // Extract mirroring if(header[INES_FLAGS6] & FLAG6_FOUR_SCREEN) { state->ines.mirroring = MIRROR_FOUR_SCREEN; } else if(header[INES_FLAGS6] & FLAG6_VERTICAL_MIRROR) { @@ -70,21 +143,20 @@ static int ines2_load(struct nes_state *state, char *path) { state->ines.prg_size = prg_size; state->ines.chr_size = chr_size; - // Skip trainer if present if(header[INES_FLAGS6] & FLAG6_TRAINER) { - fseek(f, TRAINER_SIZE, SEEK_CUR); + ptr += TRAINER_SIZE; } - // Read PRG - printf("prgsize_read: %ld\n", fread(state->prg_rom, 1, prg_size, f)); + memcpy(state->prg_rom, ptr, prg_size); + ptr += prg_size; - // Read CHR if present if(chr_size > 0) { - printf("chrsize_read: %ld\n", fread(state->chr_rom, 1, chr_size, f)); + memcpy(state->chr_rom, ptr, chr_size); } - fclose(f); + free(data); return 0; } + @@ -1,54 +1,54 @@ -#include "mapper_001.c" -// #include "mapper_mmc1.c" -// #include "mapper_uxrom.c" -#include "mapper_066.c" - -struct mapper_entry { - int 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); - void (*chr_write)(struct nes_state *state, uint32_t addr, uint8_t value); - uint8_t (*ciram_read)(struct nes_state *state, uint32_t addr); - void (*ciram_write)(struct nes_state *state, uint32_t addr, uint8_t value); - void (*tick)(struct nes_state *state); - void (*init)(struct nes_state *state); -}; +#include "mapper_0000.c" +#include "mapper_0042.c" + + +static uint8_t mapper_default_ciram_read(struct nes_state *state, uint32_t addr) { + if(state->ines.mirroring == 0) { // Horizontal + addr = (addr & 0x800) | (addr & 0x3ff); + } else { // Vertical (default fallback) + addr = addr & 0x7ff; + } + return state->ciram[addr]; +} + +static void mapper_default_ciram_write(struct nes_state *state, uint32_t addr, uint8_t value) { + if(state->ines.mirroring == 0) { + addr = (addr & 0x800) | (addr & 0x3ff); + } else { + addr = addr & 0x7ff; + } + state->ciram[addr] = value; +} + +static void mapper_default_tick(struct nes_state *state) { // No IRQ or timing logic needed +} + +/* + * Mapper numbers are constructed like this: (submapper << 12 | mapperid) both in hex. + * + * NOTE(peter): Mapper 0 always has to be first! + */ static struct mapper_entry mapper_table[] = { - { 0, mapper_001_prg_read, mapper_001_prg_write, mapper_001_chr_read, mapper_001_chr_write, mapper_001_ciram_read, mapper_001_ciram_write, mapper_001_tick, mapper_001_init }, - { 66, mapper_066_prg_read, mapper_066_prg_write, mapper_066_chr_read, mapper_066_chr_write, mapper_066_ciram_read, mapper_066_ciram_write, mapper_066_tick, mapper_066_init }, - // { 1, mapper_mmc1_read, ... }, etc +/* 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 }, }; + static void mapper_setup(struct nes_state *state) { uint32_t mapper = 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.prg_read = mapper_table[i].prg_read; - state->mapper.prg_write = mapper_table[i].prg_write; - state->mapper.chr_read = mapper_table[i].chr_read; - state->mapper.chr_write = mapper_table[i].chr_write; - state->mapper.ciram_read = mapper_table[i].ciram_read; - state->mapper.ciram_write = mapper_table[i].ciram_write; - state->mapper.tick = mapper_table[i].tick; - state->mapper.init = mapper_table[i].init; + state->mapper = mapper_table[i]; state->mapper.init(state); return; } } - // NOTE(peter): Not sure how safe this is, but it sure is funny... printf("Unsupported mapper %d, falling back to NROM\n", mapper); - state->mapper.prg_read = mapper_001_prg_read; - state->mapper.prg_write = mapper_001_prg_write; - state->mapper.chr_read = mapper_001_chr_read; - state->mapper.chr_write = mapper_001_chr_write; - state->mapper.ciram_read = mapper_001_ciram_read; - state->mapper.ciram_write = mapper_001_ciram_write; - state->mapper.tick = mapper_001_tick; - state->mapper.init = mapper_001_init; + state->mapper = mapper_table[0]; state->mapper.init(state); } + @@ -1,20 +1,20 @@ -#include "mapper_066.h" +#include "mapper_0042.h" - -struct mapper { - void (*init)(struct nes_state *state); - 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); - void (*chr_write)(struct nes_state *state, uint32_t addr, uint8_t value); - uint8_t (*ciram_read)(struct nes_state *state, uint32_t addr); - void (*ciram_write)(struct nes_state *state, uint32_t addr, uint8_t value); - void (*tick)(struct nes_state *state); +struct mapper_entry { + int 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); + void (*chr_write)(struct nes_state *state, uint32_t addr, uint8_t value); + uint8_t (*ciram_read)(struct nes_state *state, uint32_t addr); + void (*ciram_write)(struct nes_state *state, uint32_t addr, uint8_t value); + void (*tick)(struct nes_state *state); + void (*init)(struct nes_state *state); }; union mapper_data { - struct mapper_066 m066; + struct mapper_0042 m0042; }; diff --git a/mapper_0000.c b/mapper_0000.c new file mode 100644 index 0000000..e075de5 --- /dev/null +++ b/mapper_0000.c @@ -0,0 +1,24 @@ + + +static void mapper_0000_init(struct nes_state *state) { + // Nothing to initialize for 001 +} + +__attribute__((hot)) +static uint8_t mapper_0000_prg_read(struct nes_state *state, uint32_t addr) { + uint32_t prg_size = state->ines.prg_size; + + uint32_t mask = (state->ines.prg_size == 16384) ? 0x3fff : 0x7fff; + return state->prg_rom[addr & mask]; +} + +static void mapper_0000_prg_write(struct nes_state *state, uint32_t addr, uint8_t value) { +} + +__attribute__((hot)) +static uint8_t mapper_0000_chr_read(struct nes_state *state, uint32_t addr) { + return state->chr_rom[addr]; +} + +static void mapper_0000_chr_write(struct nes_state *state, uint32_t addr, uint8_t value) { +} diff --git a/mapper_001.c b/mapper_001.c deleted file mode 100644 index 4704444..0000000 --- a/mapper_001.c +++ /dev/null @@ -1,46 +0,0 @@ - - -static void mapper_001_init(struct nes_state *state) { - // Nothing to initialize for 001 -} - -static uint8_t mapper_001_prg_read(struct nes_state *state, uint32_t addr) { - uint32_t prg_size = state->ines.prg_size; - - uint32_t mask = (state->ines.prg_size == 16384) ? 0x3fff : 0x7fff; - return state->prg_rom[addr & mask]; -} - -static void mapper_001_prg_write(struct nes_state *state, uint32_t addr, uint8_t value) { -} - -static uint8_t mapper_001_chr_read(struct nes_state *state, uint32_t addr) { - return state->chr_rom[addr]; -} - -static void mapper_001_chr_write(struct nes_state *state, uint32_t addr, uint8_t value) { -} - -static uint8_t mapper_001_ciram_read(struct nes_state *state, uint32_t addr) { - if(state->ines.mirroring == 0) { // Horizontal - addr = (addr & 0x800) | (addr & 0x3ff); - } else { // Vertical (default fallback) - addr = addr & 0x7ff; - } - - return state->ciram[addr]; -} - -static void mapper_001_ciram_write(struct nes_state *state, uint32_t addr, uint8_t value) { - if(state->ines.mirroring == 0) { - addr = (addr & 0x800) | (addr & 0x3ff); - } else { - addr = addr & 0x7ff; - } - - state->ciram[addr] = value; -} - -static void mapper_001_tick(struct nes_state *state) { -} - diff --git a/mapper_0042.c b/mapper_0042.c new file mode 100644 index 0000000..4cf4c86 --- /dev/null +++ b/mapper_0042.c @@ -0,0 +1,30 @@ + +static void mapper_0042_init(struct nes_state *state) { + state->map.m0042.prg_offset = 0; + state->map.m0042.chr_offset = 0; +} + +static uint8_t mapper_0042_prg_read(struct nes_state *state, uint32_t addr) { + if(addr >= 0x8000) { + uint32_t base = state->map.m0042.prg_offset; + return state->prg_rom[base + (addr - 0x8000)]; + } + return 0; +} + +static void mapper_0042_prg_write(struct nes_state *state, uint32_t addr, uint8_t value) { + if(addr >= 0x8000) { + uint32_t prg_bank = (value >> 4) & 3; + uint32_t chr_bank = (value >> 0) & 3; + + state->map.m0042.prg_offset = prg_bank * 0x8000; + state->map.m0042.chr_offset = chr_bank * 0x2000; + } +} + +static uint8_t mapper_0042_chr_read(struct nes_state *state, uint32_t addr) { + return state->chr_rom[state->map.m0042.chr_offset + addr]; +} + +static void mapper_0042_chr_write(struct nes_state *state, uint32_t addr, uint8_t value) { +} diff --git a/mapper_066.h b/mapper_0042.h index d78f8b9..cb201d9 100644 --- a/mapper_066.h +++ b/mapper_0042.h @@ -1,5 +1,5 @@ -struct mapper_066 { +struct mapper_0042 { uint32_t prg_offset; uint32_t chr_offset; }; diff --git a/mapper_066.c b/mapper_066.c deleted file mode 100644 index 7a2b542..0000000 --- a/mapper_066.c +++ /dev/null @@ -1,58 +0,0 @@ - -static void mapper_066_init(struct nes_state *state) { - state->map.m066.prg_offset = 0; - state->map.m066.chr_offset = 0; -} - -static uint8_t mapper_066_prg_read(struct nes_state *state, uint32_t addr) { - if(addr >= 0x8000) { - uint32_t base = state->map.m066.prg_offset; - return state->prg_rom[base + (addr - 0x8000)]; - } - return 0; -} - -static void mapper_066_prg_write(struct nes_state *state, uint32_t addr, uint8_t value) { - if(addr >= 0x8000) { - uint32_t prg_bank = (value >> 4) & 3; - uint32_t chr_bank = (value >> 0) & 3; - - state->map.m066.prg_offset = prg_bank * 0x8000; - state->map.m066.chr_offset = chr_bank * 0x2000; - } -} - -static uint8_t mapper_066_chr_read(struct nes_state *state, uint32_t addr) { - return state->chr_rom[state->map.m066.chr_offset + addr]; -} - -static void mapper_066_chr_write(struct nes_state *state, uint32_t addr, uint8_t value) { -} - -static uint8_t mapper_066_ciram_read(struct nes_state *state, uint32_t addr) { - uint32_t mirrored = addr & 0x0fff; - - if(state->ines.mirroring == 0) { // Horizontal - mirrored = (mirrored & 0x800) | (mirrored & 0x3ff); - } else { // Vertical (default fallback) - mirrored = mirrored & 0x7ff; - } - - return state->ciram[mirrored]; -} - -static void mapper_066_ciram_write(struct nes_state *state, uint32_t addr, uint8_t value) { - uint32_t mirrored = addr & 0x0fff; - - if(state->ines.mirroring == 0) { - mirrored = (mirrored & 0x800) | (mirrored & 0x3ff); - } else { - mirrored = mirrored & 0x7ff; - } - - state->ciram[mirrored] = value; -} - -static void mapper_066_tick(struct nes_state *state) { - // No IRQ or timing logic needed -} @@ -1,97 +1,61 @@ +__attribute__((hot)) static uint8_t memory_read(struct nes_state *restrict state, uint32_t offset) { state->cycles++; - ppu_tick(state); ppu_tick(state); ppu_tick(state); + ppu_tick(state); - if(offset > 0xffff) { - printf("%x\n", offset); - } - - // state->ram[0x301] = 0x1; - if(offset < 0x2000) { + if(LIKELY(offset < 0x2000)) { return state->ram[offset & 0x07ff]; } else if(offset < 0x4000) { return ppu_read(state, offset); - // switch(offset & 7) { - // case 2: return ppu_read_2002(state); - // case 4: return ppu_read_2004(state); - // case 7: return ppu_read_2007(state); - // default: return 0; - // } - } else if(offset < 0x4020) { - // TODO: APU and I/O reads - return 0; - } else if(offset >= 0x6000) { + // } else if(offset < 0x4020) { + // // TODO: APU and I/O reads + // return 0; + } else if(LIKELY(offset >= 0x6000)) { return state->mapper.prg_read(state, offset); - } else { - return 0; } + return 0; } +__attribute__((hot)) static void memory_write(struct nes_state *restrict state, uint32_t offset, uint8_t value) { state->cycles++; - ppu_tick(state); ppu_tick(state); ppu_tick(state); - - if(offset > 0xffff) { - printf("%x\n", offset); - } + ppu_tick(state); - if(offset < 0x2000) { + if(LIKELY(offset < 0x2000)) { state->ram[offset & 0x07ff] = value; } else if(offset < 0x4000) { ppu_write(state, offset, value); - // switch(offset & 7) { - // case 0: ppu_write_2000(state, value); break; - // case 1: ppu_write_2001(state, value); break; - // case 3: ppu_write_2003(state, value); break; - // case 4: ppu_write_2004(state, value); break; - // case 5: ppu_write_2005(state, value); break; - // case 6: ppu_write_2006(state, value); break; - // case 7: ppu_write_2007(state, value); break; - // default: break; - // } } else if(offset == 0x4014) { ppu_dma_4014(state, value); - } else if(offset < 0x4020) { - // TODO: APU and I/O writes + // } else if(offset < 0x4020) { + // // TODO: APU and I/O writes } else if(offset >= 0x6000) { state->mapper.prg_write(state, offset, value); } } +__attribute__((hot)) static uint8_t memory_read_dma(struct nes_state *restrict state, uint32_t offset) { // Do not tick CPU/PPU/APU — caller handles timing - if(offset < 0x2000) { + if(LIKELY(offset < 0x2000)) { return state->ram[offset & 0x07ff]; - } else if(offset < 0x4000) { - // PPU register reads are ignored during DMA - return 0; - } else if(offset < 0x4020) { - // APU and I/O — usually ignored or blocked during DMA - return 0; } else if(offset >= 0x6000) { return state->mapper.prg_read(state, offset); - } else { - return 0; } + return 0; } +__attribute__((hot)) static uint8_t memory_read_dummy(struct nes_state *restrict state, uint32_t offset) { state->cycles++; - ppu_tick(state); ppu_tick(state); ppu_tick(state); + ppu_tick(state); - if(offset < 0x2000) { - return 0; - } else if(offset < 0x4000) { + if(UNLIKELY(offset >= 0x2000 && offset < 0x4000)) { return ppu_read(state, offset); - } else if(offset < 0x4020) { - return 0; - } else if(offset >= 0x6000) { - return state->mapper.prg_read(state, offset); - } else { - return 0; } + return 0; } @@ -13,6 +13,9 @@ #define WINDOW_WIDTH 320 * 3 #define WINDOW_HEIGHT 240 * 3 +#define LIKELY(x) __builtin_expect(!!(x), 1) +#define UNLIKELY(x) __builtin_expect(!!(x), 0) + #ifndef restrict # if defined(__cplusplus) # define restrict __restrict @@ -113,7 +116,7 @@ int main(int argc, char **argv) { // 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/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"); @@ -128,6 +131,9 @@ int main(int argc, char **argv) { // 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/tv.nes"); + ines2_load(&nstate, "data/Super Mario Bros. (World) (HVC-SM).zip"); + // ines2_load(&nstate, "data/Super Mario Bros. + Duck Hunt (USA).zip"); mapper_setup(&nstate); uint32_t lo = nstate.mapper.prg_read(&nstate, 0xfffc); @@ -172,7 +178,7 @@ int main(int argc, char **argv) { timer_start(timer); while(!glfwWindowShouldClose(window)) { - // timer_wait(timer); + timer_wait(timer); glfwPollEvents(); // // @@ -183,21 +189,21 @@ int main(int argc, char **argv) { nstate.ppu.frame_ready = 0; frames++; - // uint32_t * restrict dst = buffer; - // uint8_t * restrict src = nstate.ppu.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(); + uint32_t * restrict dst = buffer; + uint8_t * restrict src = nstate.ppu.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("%d\n", frames); +printf("total frames: %6.6d total cycles: %ld\n", frames, nstate.cycles); glfwDestroyWindow(window); } else { fprintf(stderr, "Failed to create window\n"); @@ -21,19 +21,33 @@ #define MIRROR_FOURSCREEN 2 struct ppu_state { - uint8_t pixels[256 * 240]; - - uint8_t oam[256]; + uint32_t scanline; + uint32_t dot; + uint32_t bg_shift_pattern_low; + uint32_t bg_shift_pattern_high; + uint32_t bg_shift_attrib_low; + uint32_t bg_shift_attrib_high; + uint8_t bg_next_tile_id; + uint8_t bg_next_tile_attrib; + uint8_t bg_next_tile_lsb; + uint8_t bg_next_tile_msb; uint8_t oam_addr; uint8_t oam_data; + + uint8_t pixels[256 * 240] __attribute__((aligned(64))); + uint8_t oam[256]; uint8_t secondary_oam[32]; + uint8_t palette[0x20]; + uint8_t sprite_indexes[8]; + uint32_t sprite_patterns[8]; + uint8_t sprite_positions[8]; + uint8_t sprite_priorities[8]; + uint8_t sprite_shift_lo[8]; + uint8_t sprite_shift_hi[8]; uint8_t reg_ctrl; uint8_t reg_mask; uint8_t reg_status; - uint8_t reg_scroll[2]; - uint8_t reg_addr[2]; - uint8_t reg_latch; uint32_t vram_addr; uint32_t temp_addr; @@ -44,33 +58,11 @@ struct ppu_state { uint8_t open_bus; - uint8_t palette[0x20]; - - uint32_t scanline; - uint32_t dot; - uint8_t even_frame; uint8_t frame_ready; - uint8_t sprite_indexes[8]; uint8_t sprite_zero_hit_possible; uint8_t sprite_count; - uint32_t sprite_patterns[8]; - uint8_t sprite_positions[8]; - uint8_t sprite_priorities[8]; - uint8_t sprite_shift_lo[8]; - uint8_t sprite_shift_hi[8]; - - uint32_t bg_shift_pattern_low; - uint32_t bg_shift_pattern_high; - uint32_t bg_shift_attrib_low; - uint32_t bg_shift_attrib_high; - - uint8_t bg_next_tile_id; - uint8_t bg_next_tile_attrib; - uint8_t bg_next_tile_lsb; - uint8_t bg_next_tile_msb; -}; - +} __attribute__((packed, aligned(64))); struct cpu_state { uint32_t pc; // Program Counter @@ -81,38 +73,39 @@ struct cpu_state { uint8_t p; // Processor Status Flags (this can be expanded with separate flags if needed) uint8_t n; // Negative Flag uint8_t v; // Overflow Flag - // uint8_t b; // Break Flag (Set by BRK instruction) + // uint8_t b; // Break Flag (Set by BRK instruction) -- does not exist outside the stack uint8_t d; // Decimal Flag uint8_t i; // Interrupt Disable Flag uint8_t z; // Zero Flag uint8_t c; // Carry Flag // -- uint8_t die; // KIL instruction found! -}; +} __attribute__((packed, aligned(64))); struct ines_state { uint32_t mapper; + uint32_t submapper; uint8_t mirroring; // 0 = H, 1 = V, 2 = 4-screen uint32_t prg_size; uint32_t chr_size; }; struct nes_state { + size_t cycles; struct ines_state ines; struct cpu_state cpu; - struct ppu_state ppu; - struct mapper mapper; - union mapper_data map; - size_t cycles; uint8_t irq_pending; uint8_t nmi_pending; - uint8_t ram[0x800]; - uint8_t sram[0x2000]; - uint8_t ciram[0x1000]; // NOTE(peter): Originally 0x800 bytes, but extended as it should work for up to fourway, this is optimization, reality is 2kb, but there is no side-effects, so this is fine! - uint8_t prg_rom[4 * 1024 * 1024]; - uint8_t chr_rom[4 * 1024 * 1024]; -}; + struct ppu_state ppu; + struct mapper_entry mapper; + union mapper_data map; + uint8_t ram[0x800] __attribute__((aligned(64))); + uint8_t sram[0x2000] __attribute__((aligned(64))); + uint8_t ciram[0x1000] __attribute__((aligned(64))); // NOTE(peter): Originally 0x800 bytes, but extended as it should work for up to fourway, this is optimization, reality is 2kb, but there is no side-effects, so this is fine! + uint8_t prg_rom[4 * 1024 * 1024] __attribute__((aligned(64))); + uint8_t chr_rom[4 * 1024 * 1024] __attribute__((aligned(64))); +} __attribute__((aligned(64))); __attribute__((aligned(4096))) static uint32_t nes_palette[64] = { @@ -17,7 +17,8 @@ static void ppu_reset(struct nes_state *state) { memset(ppu, 0, sizeof(struct ppu_state)); } -static void ppu_write(struct nes_state *state, uint32_t offset, uint8_t value) { +__attribute__((always_inline, hot)) +static inline void ppu_write(struct nes_state *state, uint32_t offset, uint8_t value) { struct ppu_state *ppu = &state->ppu; switch(offset & 7) { @@ -73,7 +74,7 @@ static void ppu_write(struct nes_state *state, uint32_t offset, uint8_t value) { state->mapper.ciram_write(state, addr, value); } else if(addr < 0x4000) { uint32_t pal_addr = addr & 0x1f; - if((pal_addr & 0x13) == 0x10) { + if((pal_addr & 3) == 0) { pal_addr &= ~0x10; } ppu->palette[pal_addr] = value; @@ -83,7 +84,8 @@ static void ppu_write(struct nes_state *state, uint32_t offset, uint8_t value) { } } -static uint8_t ppu_read(struct nes_state *state, uint32_t offset) { +__attribute__((always_inline, hot)) +static inline uint8_t ppu_read(struct nes_state *state, uint32_t offset) { struct ppu_state *ppu = &state->ppu; uint8_t result = ppu->open_bus; @@ -122,7 +124,8 @@ static uint8_t ppu_read(struct nes_state *state, uint32_t offset) { return result; } -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; @@ -155,7 +158,8 @@ static void ppu_evaluate_sprites(struct nes_state *state) { ppu->sprite_count = n; } -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; for(uint8_t i = 0; i < ppu->sprite_count; i++) { uint8_t *s = ppu->secondary_oam + i * 4; @@ -194,7 +198,8 @@ static void ppu_fetch_sprite_patterns(struct nes_state *state) { } } -static void ppu_render_pixel(struct nes_state *state) { +__attribute__((always_inline, hot)) +static inline void ppu_render_pixel(struct nes_state *state) { uint8_t bg_pixel = 0; uint8_t bg_palette = 0; uint8_t sp_pixel = 0; @@ -256,172 +261,374 @@ static void ppu_render_pixel(struct nes_state *state) { ppu->pixels[y * 256 + x] = final_color; } + __attribute__((hot, flatten)) static void ppu_tick(struct nes_state *state) { struct ppu_state *ppu = &state->ppu; - uint32_t dot = ppu->dot; - uint32_t scanline = ppu->scanline; + for(uint32_t ppu_loops = 0; ppu_loops < 3; ++ppu_loops) { + + uint32_t dot = ppu->dot; + uint32_t scanline = ppu->scanline; + + uint8_t rendering = (ppu->reg_mask & 0x18); + +#if 1 +if(rendering) { + switch(scanline) { + case 0 ... 239: { + // if(rendering && (dot >= 1 && dot <= 256)) { + // } + + switch(dot) { + case 1 ... 256: + ppu_render_pixel(state); + + + 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); + } + } - uint8_t rendering = (ppu->reg_mask & 0x18) != 0; + __attribute__((fallthrough)); + case 321 ... 336: { + + if(rendering && ((dot >= 1 && dot <= 256) || (dot >= 321 && dot <= 336))) { + 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; + } + } + } + + ppu->bg_shift_pattern_low <<= 1; + ppu->bg_shift_pattern_high <<= 1; + ppu->bg_shift_attrib_low <<= 1; + ppu->bg_shift_attrib_high <<= 1; + } - if(rendering && scanline < 240 && dot >= 1 && dot <= 256) { - ppu_render_pixel(state); - } + 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(rendering && ((dot >= 2 && dot <= 257) || (dot >= 322 && dot <= 337))) { + case 257: { + ppu->vram_addr = (ppu->vram_addr & ~0x041f) | (ppu->temp_addr & 0x041f); + ppu_evaluate_sprites(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; + case 340: { + ppu_fetch_sprite_patterns(state); + break; } } - } + } 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; + // case 241: { + // if(dot == 1) { + // ppu->reg_status |= 0x80; + // if(ppu->reg_ctrl & 0x80) { + // state->nmi_pending = 1; + // } + // } + // } break; + + // case 261: { + // if(dot == 1) { + // ppu->reg_status &= ~0x80; + // ppu->reg_status &= ~0x40; + // ppu->sprite_zero_hit_possible = 0; + // } + + // if(dot >= 280 && dot <= 304) { + // ppu->vram_addr = (ppu->vram_addr & ~0x7be0) | (ppu->temp_addr & 0x7be0); + // } + + // if(dot == 340) { + // ppu_fetch_sprite_patterns(state); + // } + // } break; + + // // Handle the frame rendering + // if(++dot > 340) { + // dot = 0; + // scanline++; + // if(scanline > 261) { + // scanline = 0; + // ppu->frame_ready = 1; + // } + // } + + // ppu->dot = dot; + // ppu->scanline = scanline; } +} + +#else + + // if(ppu->even_frame && (ppu->reg_mask & 0x18)) { + // // skip this dot + // // call mapper_tick here. + // ppu->dot++; + // } + + + if(rendering && scanline < 240 && dot >= 1 && dot <= 256) { + ppu_render_pixel(state); + } - if(scanline < 240 || scanline == 261) { if(rendering && ((dot >= 1 && dot <= 256) || (dot >= 321 && dot <= 336))) { - 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; + + 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; + } } - 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; + } + + ppu->bg_shift_pattern_low <<= 1; + ppu->bg_shift_pattern_high <<= 1; + ppu->bg_shift_attrib_low <<= 1; + ppu->bg_shift_attrib_high <<= 1; + } + + + if(scanline < 240 || scanline == 261) { + if(rendering && ((dot >= 1 && dot <= 256) || (dot >= 321 && dot <= 336))) { + 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; + } } - 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; + } + + if(rendering) { + 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 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; + + if(dot == 257) { + ppu->vram_addr = (ppu->vram_addr & ~0x041f) | (ppu->temp_addr & 0x041f); } - 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(UNLIKELY(scanline == 261) && dot >= 280 && dot <= 304) { + ppu->vram_addr = (ppu->vram_addr & ~0x7be0) | (ppu->temp_addr & 0x7be0); + } - if((ppu->vram_addr & 0x001f) == 31) { - ppu->vram_addr &= ~0x001f; - ppu->vram_addr ^= 0x0400; - } else { - ppu->vram_addr++; - } + if(dot == 257 && LIKELY(scanline < 240)) { + ppu_evaluate_sprites(state); + } - break; + if(dot == 340 && (LIKELY(scanline < 240) || UNLIKELY(scanline == 261))) { + ppu_fetch_sprite_patterns(state); } } } +#endif - if(rendering) { - 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); + + +// TEST SWITCH CODE + + switch(scanline) { + case 241: { + if(dot == 1) { + ppu->reg_status |= 0x80; + if(ppu->reg_ctrl & 0x80) { + state->nmi_pending = 1; } } + break; + } - if(dot == 257) { - ppu->vram_addr = (ppu->vram_addr & ~0x041f) | (ppu->temp_addr & 0x041f); + case 261: { + if(dot == 1) { + ppu->reg_status &= ~0x80; + ppu->reg_status &= ~0x40; + ppu->sprite_zero_hit_possible = 0; } - if(scanline == 261 && dot == 304) { //>= 280 && dot <= 304) { + if(dot >= 280 && dot <= 304) { ppu->vram_addr = (ppu->vram_addr & ~0x7be0) | (ppu->temp_addr & 0x7be0); } - if(dot == 257 && scanline < 240) { - ppu_evaluate_sprites(state); - } - - if(dot == 340 && scanline < 240) { + if(dot == 340) { ppu_fetch_sprite_patterns(state); } + break; } } - if(scanline == 241 && dot == 1) { - ppu->reg_status |= 0x80; - if(ppu->reg_ctrl & 0x80) { - state->nmi_pending = 1; - } - } - - if(scanline == 261 && dot == 1) { - ppu->reg_status &= ~0x80; - ppu->reg_status &= ~0x40; - ppu->sprite_zero_hit_possible = 0; - } - dot++; - if(dot > 340) { - dot = 0; - scanline++; - if(scanline > 261) { - scanline = 0; - ppu->frame_ready = 1; +// TEST SWITCH CODE + + // if(UNLIKELY(scanline == 241) && dot == 1) { + // ppu->reg_status |= 0x80; + // if(ppu->reg_ctrl & 0x80) { + // state->nmi_pending = 1; + // } + // } + + // if(UNLIKELY(scanline == 261) && dot == 1) { + // ppu->reg_status &= ~0x80; + // ppu->reg_status &= ~0x40; + // ppu->sprite_zero_hit_possible = 0; + // } + + dot++; + if(dot > 340) { + dot = 0; + scanline++; + if(scanline > 261) { + scanline = 0; + ppu->frame_ready = 1; + } } - } - ppu->dot = dot; - ppu->scanline = scanline; + ppu->dot = dot; + ppu->scanline = scanline; + } } -static void ppu_dma_4014(struct nes_state *state, uint8_t page) { +__attribute__((always_inline, hot)) +static inline void ppu_dma_4014(struct nes_state *state, uint8_t page) { uint32_t base = page << 8; // Add 1 or 2 idle cycles depending on current CPU cycle uint8_t idle_cycles = (state->cycles & 1) ? 1 : 2; for(uint8_t i = 0; i < idle_cycles; i++) { state->cycles++; - ppu_tick(state); ppu_tick(state); ppu_tick(state); + ppu_tick(state); } for(uint32_t i = 0; i < 256; i++) { uint32_t addr = base + i; state->cycles++; - ppu_tick(state); ppu_tick(state); ppu_tick(state); + ppu_tick(state); uint8_t value = memory_read_dma(state, addr); state->cycles++; - ppu_tick(state); ppu_tick(state); ppu_tick(state); + ppu_tick(state); // ppu_write_2004(state, value); ppu_write(state, 4, value); } |
