1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
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 ines2_load(struct nes_state *state, 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);
state->ines.mapper = mapper_low | mapper_high | (mapper_ext << 8);
// 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
printf("prgsize_read: %ld\n", fread(state->prg_rom, 1, prg_size, f));
// Read CHR if present
if(chr_size > 0) {
printf("chrsize_read: %ld\n", fread(state->chr_rom, 1, chr_size, f));
}
fclose(f);
return 0;
}
|