const types = @import("types.zig"); const Mirroring = types.Mirroring; const Mapper65 = @This(); prg_banks: [3]u8 = [_]u8{ 0, 1, 2 }, chr_banks: [8]u8 = [_]u8{ 0, 1, 2, 3, 4, 5, 6, 7 }, mirroring_mode: Mirroring = .vertical, irq_enabled: bool = false, irq_counter: u16 = 0, irq_reload: u16 = 0, irq_assert: bool = false, prg_bank_count: usize, chr_bank_count: usize, pub fn init( prg_size: usize, chr_size: usize, mirroring_mode: Mirroring, ) Mapper65 { return .{ .prg_bank_count = prg_size / 0x2000, .chr_bank_count = if (chr_size > 0) chr_size / 0x0400 else 1, .mirroring_mode = mirroring_mode, }; } pub fn cpuMapRead( self: *const Mapper65, address: u16, ) ?usize { if (address < 0x8000) return null; const offset = @as(usize, address & 0x1fff); const last_bank = self.prg_bank_count - 1; switch ((address - 0x8000) / 0x2000) { 0 => return (@as(usize, self.prg_banks[0]) % self.prg_bank_count) * 0x2000 + offset, 1 => return (@as(usize, self.prg_banks[1]) % self.prg_bank_count) * 0x2000 + offset, 2 => return (@as(usize, self.prg_banks[2]) % self.prg_bank_count) * 0x2000 + offset, 3 => return last_bank * 0x2000 + offset, else => unreachable, } } pub fn cpuWrite( self: *Mapper65, address: u16, value: u8, ) void { if (address < 0x8000) return; switch (address) { 0x8000 => self.prg_banks[0] = value, 0xa000 => self.prg_banks[1] = value, 0xc000 => self.prg_banks[2] = value, 0x9001 => self.mirroring_mode = if ((value & 0x80) != 0) .horizontal else .vertical, 0x9003 => { self.irq_enabled = (value & 0x80) != 0; self.irq_assert = false; }, 0x9004 => { self.irq_reload = (self.irq_reload & 0xff00) | value; }, 0x9005 => { self.irq_reload = (self.irq_reload & 0x00ff) | (@as(u16, value) << 8); self.irq_counter = self.irq_reload; self.irq_assert = false; }, 0xb000 => self.chr_banks[0] = value, 0xb001 => self.chr_banks[1] = value, 0xb002 => self.chr_banks[2] = value, 0xb003 => self.chr_banks[3] = value, 0xb004 => self.chr_banks[4] = value, 0xb005 => self.chr_banks[5] = value, 0xb006 => self.chr_banks[6] = value, 0xb007 => self.chr_banks[7] = value, else => {}, } } pub fn ppuMapRead( self: *const Mapper65, address: u16, ) ?usize { if (address >= 0x2000) return null; const slot = address / 0x0400; const offset = @as(usize, address & 0x03ff); const bank = @as(usize, self.chr_banks[slot]) % self.chr_bank_count; return bank * 0x0400 + offset; } pub fn ppuMapWrite( self: *const Mapper65, address: u16, ) ?usize { return self.ppuMapRead(address); } pub fn mirroring( self: *const Mapper65, ) Mirroring { return self.mirroring_mode; } pub fn irqAsserted( self: *const Mapper65, ) bool { return self.irq_assert; }