summaryrefslogtreecommitdiff
path: root/ines2.c
blob: 7e3c17e07041b23a23f6d3a59b5029cc3d612470 (plain)
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->rom, 1, prg_size, f));

	// Read CHR if present
	if(chr_size > 0) {
		printf("chrsize_read: %ld\n", fread(state->chrrom, 1, chr_size, f));
	}

	fclose(f);
	return 0;
}