summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Fors <peter.fors@mindkiller.com>2025-04-05 08:58:12 +0200
committerPeter Fors <peter.fors@mindkiller.com>2025-04-05 08:58:12 +0200
commitf1bd6a7d2f4ffe3e5263e0254bcf7522ab381264 (patch)
treee75bde292329f337d619f9a997aab9b17c37e38b
parent8c82be43720d9e221a9e2541c9ff6151015838bb (diff)
transform to switch case for ppu_tick()
-rwxr-xr-xaddmapper.py67
-rwxr-xr-xbuild.sh4
-rw-r--r--cpu.c11
-rw-r--r--ines2.c122
-rw-r--r--mapper.c72
-rw-r--r--mapper.h24
-rw-r--r--mapper_0000.c24
-rw-r--r--mapper_001.c46
-rw-r--r--mapper_0042.c30
-rw-r--r--mapper_0042.h (renamed from mapper_066.h)2
-rw-r--r--mapper_066.c58
-rw-r--r--memory.c76
-rw-r--r--mknes.c36
-rw-r--r--mknes.h75
-rw-r--r--ppu.c435
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)
+
diff --git a/build.sh b/build.sh
index 348700d..5487439 100755
--- a/build.sh
+++ b/build.sh
@@ -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=""
diff --git a/cpu.c b/cpu.c
index eb7c837..263ee6e 100644
--- a/cpu.c
+++ b/cpu.c
@@ -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);
-
}
diff --git a/ines2.c b/ines2.c
index 9fb743d..dc0e57d 100644
--- a/ines2.c
+++ b/ines2.c
@@ -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;
}
+
diff --git a/mapper.c b/mapper.c
index a7954b1..09d9a62 100644
--- a/mapper.c
+++ b/mapper.c
@@ -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);
}
+
diff --git a/mapper.h b/mapper.h
index 46db0a4..e7e1437 100644
--- a/mapper.h
+++ b/mapper.h
@@ -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
-}
diff --git a/memory.c b/memory.c
index e0ed85a..28c51ad 100644
--- a/memory.c
+++ b/memory.c
@@ -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;
}
diff --git a/mknes.c b/mknes.c
index 0ca6749..9a91376 100644
--- a/mknes.c
+++ b/mknes.c
@@ -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");
diff --git a/mknes.h b/mknes.h
index ed22652..b8bf87f 100644
--- a/mknes.h
+++ b/mknes.h
@@ -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] = {
diff --git a/ppu.c b/ppu.c
index aff5166..02de73a 100644
--- a/ppu.c
+++ b/ppu.c
@@ -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);
}