// 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; }