22#pragma description Nintendo Entertainment System ROM (.nes)
33#pragma MIME application/x-nes-rom
44
5- import std.mem;
65import std.string;
76
8- bitfield iNES07Flags {
9- mirroringIsVertical : 1;
10- batterybackedPRGRAM : 1;
11- trainerOf512Bytes : 1;
12- ignoreMirroring : 1;
13- mapperLowerNybble : 4;
14- vsUnisystem : 1;
15- playchoice10 : 1;
16- nes2Format : 2 [[name("nes2.0Format")]];
17- mapperHigherNybble : 4;
18- } [[name("iNES0.7Flags")]];
19-
20- bitfield Flags9 {
21- isPAL : 1;
22- padding : 7;
23- };
24-
25- bitfield Flags10 {
26- tvSystem : 2;
27- padding : 2;
28- prgRAM : 1;
29- busConflicts : 1;
30- padding : 2;
31- };
32-
33- struct iNESFlags {
34- iNES07Flags ines07Flags [[name("ines0.7Flags")]];
35- if (!ines07Flags.nes2Format) {
36- u8 prgRAM8KBMultiplier;
37- Flags9 flags9;
38- Flags10 flags10;
39- restLength = 5;
40- }
41- else {
42- restLength = 9;
43- }
44- };
45-
46- u8 restLength;
7+ bitfield Flags {
8+ mirroringIsHorizontal : 1;
9+ ignoreMirroring : 1;
10+ batterybackedPRGRAM : 1;
11+ trainerOf512Bytes : 1;
12+ lowerMapperNybble : 4;
13+ };
14+
15+ enum ConsoleType : u8 {
16+ Regular,
17+ VsSystem,
18+ PlayChoice10,
19+ ExtendedConsoleType
20+ };
21+
22+ fn consoleType(u8 bits) {
23+ ConsoleType type = bits;
24+ return type;
25+ };
26+
27+ fn nes2Format(u8 bits) {
28+ return std::string::to_string(bits == 2) + " (" + std::string::to_string(bits) + ")";
29+ };
30+
31+ bitfield iNESFlags7 {
32+ consoleType : 2 [[format("consoleType")]];
33+ nes2Format : 2 [[format("nes2Format"), name("nes2.0Format")]];
34+ higherMapperNybble : 4;
35+ };
36+
37+ bitfield iNESFlags9 {
38+ tvSystem : 1 [[comment("0 = NTSC, 1 = PAL")]];
39+ padding : 7;
40+ };
41+
42+ fn formatDualTVSystem(u8 bits) {
43+ match (bits) {
44+ (0): return "NTSC";
45+ (2): return "PAL";
46+ (1 || 3): return "Dual Compatible";
47+ }
48+ };
49+
50+ bitfield iNESFlags10 {
51+ dualTVSystem : 2 [[format("formatDualTVSystem")]];
52+ padding : 2;
53+ prgRAM : 1;
54+ busConflicts : 1;
55+ };
56+
57+ bitfield MapperExtra {
58+ highestMapperNybble : 4;
59+ submapper : 4;
60+ };
61+
62+ bitfield ROMSize {
63+ extraPRGROMSize : 4;
64+ extraCHRROMSize : 4;
65+ };
66+
67+ bitfield PRGRAMSize {
68+ prgRAMShiftCount : 4;
69+ eepromShiftCount : 4 [[comment("EEPROM = Non-volatile PRG RAM")]];
70+ };
71+
72+ bitfield CHRRAMSize {
73+ chrRAMSizeShiftCount : 4;
74+ chrNVRAMSizeShiftCount : 4;
75+ };
76+
77+ enum TimingList : u8 {
78+ NTSC,
79+ PAL,
80+ MultiRegion,
81+ Dendy
82+ };
83+
84+ fn Timing(u8 value) {
85+ TimingList type = value;
86+ return type;
87+ };
88+
89+ bitfield Timing {
90+ processorTiming : 2 [[format("Timing")]];
91+ padding : 6;
92+ };
93+
94+ bitfield VsSystemType {
95+ vsPPUType : 4;
96+ vsHardwareType: 4;
97+ };
98+
99+ enum ExtendedConsoleType : ConsoleType {
100+ DecimalModeFamiclone = 3,
101+ PlugThrough,
102+ VT01,
103+ VT02,
104+ VT03,
105+ VT09,
106+ VT32,
107+ VT3xx,
108+ UM6578,
109+ FamicomNetworkSystem
110+ };
111+
112+ fn formatExtendedConsoleType(u8 nybble) {
113+ ExtendedConsoleType type = nybble;
114+ return type;
115+ };
116+
117+ bitfield ExtendedConsoleTypeByte {
118+ type : 4 [[format("formatExtendedConsoleType")]];
119+ padding : 4;
120+ };
121+
122+ bitfield MiscellaneousROMs {
123+ numberOfMiscellaneousROMs : 2;
124+ padding : 6;
125+ };
126+
127+ bitfield DefaultExpansionDevice {
128+ defaultExpansionDevice : 6;
129+ };
130+
131+ struct NES2Attributes {
132+ MapperExtra mapperExtra;
133+ ROMSize romSize;
134+ PRGRAMSize prgRAMSize;
135+ CHRRAMSize chrRAMSize;
136+ Timing timing;
137+ if (parent.inesFlags7.consoleType == ConsoleType::VsSystem) {
138+ VsSystemType vsSystemType;
139+ }
140+ else if (parent.inesFlags7.consoleType == ConsoleType::ExtendedConsoleType) {
141+ ExtendedConsoleTypeByte ExtendedConsoleTypeByte;
142+ }
143+ else {
144+ padding[1];
145+ }
146+ MiscellaneousROMs miscellaneousROMs;
147+ DefaultExpansionDevice defaultExpansionDevice;
148+ };
149+
150+ fn renderEOF(str string) {
151+ return "\"NES<EOF>\"";
152+ };
47153
48154struct Header {
49- char identifier[4];
50- u8 prgROM16KBMultiplier;
51- u8 chrROM8KBMultiplier;
52- iNESFlags inesFlags;
53- char rest[restLength];
155+ char identifier[4] [[format("renderEOF")]];
156+ u8 prgROMSizeBy16KiBs;
157+ u8 chrROMSizeBy8KiBs;
158+ Flags flags;
159+ if ($[0x07] & 12 != 4) {
160+ iNESFlags7 inesFlags7;
161+ if (inesFlags7.nes2Format)
162+ NES2Attributes nes2Attributes;
163+ else if ($[0x07] & 12 == 0 && !std::mem::read_unsigned($, 4)) {
164+ u8 prgRAMSizeBy8KiBs;
165+ iNESFlags9 inesFlags9;
166+ iNESFlags10 inesFlags10;
167+ }
168+ }
54169};
55170
56171Header header @ 0x00;
57172
173+ u8 trainer[512*header.flags.trainerOf512Bytes] @ 0x10;
174+
175+ enum CHRType : u8 {
176+ CHRROM,
177+ CHRRAM
178+ };
179+
180+ fn chrType(u8 value) {
181+ CHRType enumValue = value;
182+ return enumValue;
183+ };
184+
185+ fn chrSize(u8 value) {
186+ u24 actualSize;
187+ if (value == 4) actualSize = 262144;
188+ else actualSize = 8192 * header.chrROMSizeBy8KiBs;
189+ return std::string::to_string(value) + " (" + std::string::to_string(actualSize) + ")";
190+ };
191+
192+ bitfield MemorySize {
193+ prgROMSizeBy16KiBs : 4;
194+ chrType : 1 [[format("chrType")]];
195+ chrSize : 3 [[format("chrSize")]];
196+ };
197+
198+ enum ArrangementList : u8 {
199+ Horizontal,
200+ Vertical
201+ };
202+
203+ fn arrangement(u8 value) {
204+ ArrangementList enumValue = value;
205+ return enumValue;
206+ };
207+
208+ enum MapperList : u8 {
209+ NROM,
210+ CNROM,
211+ UNROM,
212+ GNROM,
213+ MMC
214+ };
215+
216+ fn mapper(u8 value) {
217+ MapperList enumValue = value;
218+ return enumValue;
219+ };
220+
221+ bitfield CartridgeType {
222+ nametableArrangement : 1 [[format("arrangement")]];
223+ mapper : 7 [[format("mapper")]];
224+ };
225+
58226enum EncodingType : u8 {
59- ASCII = 1
227+ None,
228+ ASCII,
229+ JIS
60230};
61231
232+ fn titleLength(u8 value) { return value+1; };
233+
62234struct OfficialHeader {
63- char title[16] [[hex::spec_name("Title Registration Area")]];
64- u16 programChecksum;
65- u16 characterChecksum;
66- u8 memorySize [[hex::spec_name("Cartridge Memory Size")]];
67- u8 cartridgeType;
68- EncodingType encodingType [[hex::spec_name("Registration Character Type Distinction")]];
69- u8 titleLength [[hex::spec_name("Registration Characters Count")]];
70- u8 makerID [[hex::spec_name("Maker Code")]];
71- u8 complementaryChecksum;
235+ char title[16] [[hex::spec_name("Title Registration Area")]];
236+ u16 programChecksum;
237+ u16 characterChecksum;
238+ MemorySize memorySize [[hex::spec_name("Cartridge Memory Size")]];
239+ CartridgeType cartridgeType;
240+ EncodingType encodingType [[hex::spec_name("Registration Characters Type Distinction")]];
241+ u8 titleLength [[hex::spec_name("Registration Characters Count"), transform("titleLength ")]];
242+ u8 licenseeID [[hex::spec_name("Maker Code")]];
243+ u8 complementaryChecksum [[hex::spec_name("Checksum for characterChecksum~makerID")]] ;
72244};
73245
74- union OfficialHeaderUnion {
75- u8 miscellaneousData[26];
76- OfficialHeader officialHeader;
246+ u24 calculatedPRGROMSize = 16384 * ((0x0100 * $[9] & 0x0F) * ($[7] & 12 == 8) + header.prgROMSizeBy16KiBs);
247+
248+ fn hasOfficialHeader() {
249+ u8 sum;
250+ for (u8 i = 0, i < 8, i += 1) {
251+ sum += $[(calculatedPRGROMSize - 14) + i];
252+ }
253+ return !sum;
77254};
78255
79256struct PRGROM {
80- u8 data[16384*header.prgROM16KBMultiplier-32];
81- OfficialHeaderUnion officialHeaderUnion;
82- u16 nmi;
83- u16 entryPoint;
84- u16 externalIRQ;
257+ u8 data[calculatedPRGROMSize - 26 * hasOfficialHeader() - 6];
258+ if (hasOfficialHeader())
259+ OfficialHeader officialHeader;
260+ u16 nmi;
261+ u16 resetVector [[comment("Entry Point")]];
262+ u16 externalIRQ;
85263};
86264
87- PRGROM prgROM @ sizeof(header );
88- u8 chrROM[8192* header.chrROM8KBMultiplier ] @ sizeof(header)+sizeof( prgROM);
265+ PRGROM prgROM @ 0x10 + sizeof(trainer );
266+ u8 chrROM[8192 * ((0x0100 * $[9] >> 4) * ($[7] & 12 == 8) + header.chrROMSizeBy8KiBs) ] @ addressof( prgROM) + 16384 * header.prgROMSizeBy16KiBs ;
0 commit comments