diff --git a/src/system/nes/cartridge.zig b/src/system/nes/cartridge.zig index 568a5a0..e15f4ff 100644 --- a/src/system/nes/cartridge.zig +++ b/src/system/nes/cartridge.zig @@ -53,19 +53,33 @@ pub fn init( const flags6 = rom[6]; const flags7 = rom[7]; - // NES 2.0 signature. - if ((flags7 & 0x0c) == 0x08) - return error.UnsupportedNes2; + const is_nes2 = (flags7 & 0x0c) == 0x08; - const mapper_id: u8 = - (flags6 >> 4) | - (flags7 & 0xf0); + var mapper_id: u16 = (flags6 >> 4) | (flags7 & 0xf0); + var prg_size: usize = @as(usize, prg_banks) * 0x4000; + var chr_size: usize = @as(usize, chr_banks) * 0x2000; - const prg_size = - @as(usize, prg_banks) * 0x4000; + if (is_nes2) { + mapper_id |= @as(u16, rom[8] & 0x0f) << 8; - const chr_size = - @as(usize, chr_banks) * 0x2000; + const prg_msb = rom[9] & 0x0f; + 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) { 0, 3 => { @@ -177,21 +191,21 @@ pub fn init( .vrc4 = _mapper.Vrc4.init( prg_size, chr_size, - mapper_id, + @truncate(mapper_id), ), }, 22, 23 => .{ .vrc2 = _mapper.Vrc2.init( prg_size, chr_size, - mapper_id, + @truncate(mapper_id), ), }, 24, 26 => .{ .vrc6 = _mapper.Vrc6.init( prg_size, chr_size, - mapper_id, + @truncate(mapper_id), ), }, 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, 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); +}