const std = @import("std"); const common = @import("../common.zig"); const Mirroring = common.Mirroring; // ponytail: Mapper 34 (BNROM / NINA-001) - 32KB PRG banking, optional NINA-001 4KB CHR banking pub const Bnrom = @This(); prg_bank: u8 = 0, chr_bank_0: u8 = 0, chr_bank_1: u8 = 1, mirroring_mode: Mirroring, pub fn init(initial_mirroring: Mirroring) Bnrom { return .{ .mirroring_mode = initial_mirroring }; } pub fn cpuRead(self: *const Bnrom, address: u16, prg_rom: []const u8, prg_ram: []const u8) ?u8 { if (address >= 0x6000 and address <= 0x7fff) { const offset = address - 0x6000; if (offset < prg_ram.len) return prg_ram[offset]; return 0; } if (address >= 0x8000 and address <= 0xffff) { const total_32k_banks = @max(1, prg_rom.len / 0x8000); const bank = @as(usize, self.prg_bank) % total_32k_banks; const offset = (bank * 0x8000) + (address - 0x8000); if (offset < prg_rom.len) return prg_rom[offset]; return 0; } return null; } pub fn cpuWrite(self: *Bnrom, address: u16, value: u8, prg_ram: []u8) bool { if (address >= 0x6000 and address <= 0x7fff) { // NINA-001 registers switch (address) { 0x7ffd => { self.prg_bank = value & 0x0f; return true; }, 0x7ffe => { self.chr_bank_0 = value & 0x0f; return true; }, 0x7fff => { self.chr_bank_1 = value & 0x0f; return true; }, else => { const offset = address - 0x6000; if (offset < prg_ram.len) { prg_ram[offset] = value; return true; } }, } } if (address >= 0x8000 and address <= 0xffff) { // BNROM: select 32KB PRG bank self.prg_bank = value & 0x0f; return true; } return false; } pub fn ppuRead(self: *const Bnrom, address: u16, chr_rom: []const u8, chr_ram: []const u8, chr_is_ram: bool) ?u8 { if (address < 0x2000) { if (chr_is_ram) { if (address < chr_ram.len) return chr_ram[address]; } else if (chr_rom.len > 0) { const total_4k_banks = @max(1, chr_rom.len / 0x1000); const bank = if (address < 0x1000) @as(usize, self.chr_bank_0) % total_4k_banks else @as(usize, self.chr_bank_1) % total_4k_banks; const offset = (bank * 0x1000) + (address & 0x0fff); if (offset < chr_rom.len) return chr_rom[offset]; } return 0; } return null; } pub fn ppuWrite(_: *Bnrom, address: u16, value: u8, chr_ram: []u8, chr_is_ram: bool) bool { if (address < 0x2000 and chr_is_ram) { if (address < chr_ram.len) { chr_ram[address] = value; return true; } } return false; } pub fn mirroring(self: *const Bnrom) Mirroring { return self.mirroring_mode; } pub fn irqLine(_: *const Bnrom) bool { return false; } pub fn notifyPpuAddress(_: *Bnrom, _: u16) void {} test "bnrom bank switching" { var bn = Bnrom.init(.vertical); var ram: [0x2000]u8 = undefined; _ = bn.cpuWrite(0x8000, 3, &ram); try std.testing.expectEqual(@as(u8, 3), bn.prg_bank); }