summaryrefslogtreecommitdiff
path: root/ines2.c
diff options
context:
space:
mode:
Diffstat (limited to 'ines2.c')
-rw-r--r--ines2.c90
1 files changed, 90 insertions, 0 deletions
diff --git a/ines2.c b/ines2.c
new file mode 100644
index 0000000..450c22e
--- /dev/null
+++ b/ines2.c
@@ -0,0 +1,90 @@
+
+
+
+// iNES header fields
+#define INES_HEADER_SIZE 16
+#define INES_PRG_SIZE_LSB 4
+#define INES_CHR_SIZE_LSB 5
+#define INES_FLAGS6 6
+#define INES_FLAGS7 7
+#define INES_PRG_CHR_MSB 9
+#define INES_CHR_EXP 8
+#define INES_PRG_EXP 9
+
+// Size units
+#define PRG_ROM_UNIT 16384
+#define CHR_ROM_UNIT 8192
+#define TRAINER_SIZE 512
+
+// Flag masks
+#define FLAG6_TRAINER 0x04
+#define FLAG6_FOUR_SCREEN 0x08
+#define FLAG6_VERTICAL_MIRROR 0x01
+
+// Mirroring modes
+#define MIRROR_HORIZONTAL 0
+#define MIRROR_VERTICAL 1
+#define MIRROR_FOUR_SCREEN 2
+
+
+static int load_ines2(struct nes_state *state, const char *path) {
+ FILE *f = fopen(path, "rb");
+ if(!f) {
+ return -1;
+ }
+
+ uint8_t header[INES_HEADER_SIZE];
+ fread(header, 1, INES_HEADER_SIZE, f);
+
+ uint8_t prg_lsb = header[INES_PRG_SIZE_LSB];
+ uint8_t chr_lsb = header[INES_CHR_SIZE_LSB];
+ uint8_t prg_msb = header[INES_PRG_CHR_MSB] & 0x0f;
+ uint8_t chr_msb = (header[INES_PRG_CHR_MSB] & 0xf0) >> 4;
+
+ 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];
+ }
+
+ // 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) << 8;
+ state->ines.mapper = mapper_low | mapper_high | mapper_ext;
+
+ // Extract mirroring
+ if(header[INES_FLAGS6] & FLAG6_FOUR_SCREEN) {
+ state->ines.mirroring = MIRROR_FOUR_SCREEN;
+ } else if(header[INES_FLAGS6] & FLAG6_VERTICAL_MIRROR) {
+ state->ines.mirroring = MIRROR_VERTICAL;
+ } else {
+ state->ines.mirroring = MIRROR_HORIZONTAL;
+ }
+
+ 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);
+ }
+
+ // Read PRG
+ fread(state->rom, 1, prg_size, f);
+
+ // Read CHR if present
+ if(chr_size > 0) {
+ fread(state->chrrom, 1, chr_size, f);
+ }
+
+ fclose(f);
+ return 0;
+}
+
+