add NES 2.0 cartridge header parsing support

This commit is contained in:
2026-08-14 16:32:55 +02:00
parent 02a29b12b2
commit d7cca04cd4
+39 -13
View File
@@ -53,19 +53,33 @@ pub fn init(
const flags6 = rom[6]; const flags6 = rom[6];
const flags7 = rom[7]; const flags7 = rom[7];
// NES 2.0 signature. const is_nes2 = (flags7 & 0x0c) == 0x08;
if ((flags7 & 0x0c) == 0x08)
return error.UnsupportedNes2;
const mapper_id: u8 = var mapper_id: u16 = (flags6 >> 4) | (flags7 & 0xf0);
(flags6 >> 4) | var prg_size: usize = @as(usize, prg_banks) * 0x4000;
(flags7 & 0xf0); var chr_size: usize = @as(usize, chr_banks) * 0x2000;
const prg_size = if (is_nes2) {
@as(usize, prg_banks) * 0x4000; mapper_id |= @as(u16, rom[8] & 0x0f) << 8;
const chr_size = const prg_msb = rom[9] & 0x0f;
@as(usize, chr_banks) * 0x2000; if (prg_msb < 0x0f) {
prg_size = ((@as(usize, prg_msb) << 8) | prg_banks) * 0x4000;
} else {
const exponent = (prg_banks >> 2) & 0x3f;
const multiplier = prg_banks & 0x03;
prg_size = (@as(usize, 1) << @intCast(exponent)) * (multiplier * 2 + 1);
}
const chr_msb = (rom[9] >> 4) & 0x0f;
if (chr_msb < 0x0f) {
chr_size = ((@as(usize, chr_msb) << 8) | chr_banks) * 0x2000;
} else {
const exponent = (chr_banks >> 2) & 0x3f;
const multiplier = chr_banks & 0x03;
chr_size = (@as(usize, 1) << @intCast(exponent)) * (multiplier * 2 + 1);
}
}
switch (mapper_id) { switch (mapper_id) {
0, 3 => { 0, 3 => {
@@ -177,21 +191,21 @@ pub fn init(
.vrc4 = _mapper.Vrc4.init( .vrc4 = _mapper.Vrc4.init(
prg_size, prg_size,
chr_size, chr_size,
mapper_id, @truncate(mapper_id),
), ),
}, },
22, 23 => .{ 22, 23 => .{
.vrc2 = _mapper.Vrc2.init( .vrc2 = _mapper.Vrc2.init(
prg_size, prg_size,
chr_size, chr_size,
mapper_id, @truncate(mapper_id),
), ),
}, },
24, 26 => .{ 24, 26 => .{
.vrc6 = _mapper.Vrc6.init( .vrc6 = _mapper.Vrc6.init(
prg_size, prg_size,
chr_size, chr_size,
mapper_id, @truncate(mapper_id),
), ),
}, },
34 => .{ 34 => .{
@@ -1103,3 +1117,15 @@ test "NES Cartridge - Nintendo MMC5 Mapper 5 PRG, CHR Banking & Multiplier" {
try std.testing.expectEqual(@as(?u8, 0xd2), mmc5.readRegister(0x5205)); try std.testing.expectEqual(@as(?u8, 0xd2), mmc5.readRegister(0x5205));
try std.testing.expectEqual(@as(?u8, 0x00), mmc5.readRegister(0x5206)); try std.testing.expectEqual(@as(?u8, 0x00), mmc5.readRegister(0x5206));
} }
test "NES Cartridge - NES 2.0 Header Parsing" {
// Header for 128KB PRG, 128KB CHR, Mapper 1 (MMC1), NES 2.0 signature (flags7 0x08)
const nes2_header = [_]u8{
'N', 'E', 'S', 0x1A, 0x08, 0x10, 0x13, 0x08,
0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00,
} ++ ([_]u8{0} ** (128 * 1024 + 128 * 1024));
const cart = try Cartridge.init(&nes2_header);
try std.testing.expectEqual(@as(usize, 128 * 1024), cart.prg_rom.len);
try std.testing.expectEqual(@as(usize, 128 * 1024), cart.chr_rom.len);
}