const Cartridge = @import("cartridge.zig"); const Controller = @import("controller.zig"); const Ppu = @import("ppu.zig"); const Apu = @import("apu.zig"); const Bus = @This(); ram: [0x800]u8 = [_]u8{0} ** 0x800, cartridge: Cartridge, ppu: Ppu = .{}, apu: Apu = .{}, controllers: [2]Controller = .{ .{}, .{}, }, open_bus: u8 = 0, oam_dma_page: ?u8 = null, pub fn init( rom: []const u8, ) Cartridge.Error!Bus { return .{ .cartridge = try Cartridge.init(rom), }; } pub fn reset( self: *Bus, ) void { self.ppu.reset(); self.apu.reset(); for (&self.controllers) |*controller| controller.reset(); self.oam_dma_page = null; } pub inline fn read( self: *Bus, address: u16, ) u8 { const value: u8 = switch (address) { // 2 KiB internal RAM + mirrors. 0x0000...0x1fff => self.ram[address & 0x07ff], // PPU registers + mirrors. 0x2000...0x3fff => self.ppu.cpuRead( &self.cartridge, address & 7, ), // APU status. 0x4015 => self.apu.readStatus(), // Controller 1. 0x4016 => self.controllers[0].read(), // Controller 2. 0x4017 => self.controllers[1].read(), // Cartridge space. 0x4020...0xffff => self.cartridge.cpuRead(address), else => self.open_bus, }; self.open_bus = value; return value; } pub inline fn write( self: *Bus, address: u16, value: u8, ) void { self.open_bus = value; switch (address) { 0x0000...0x1fff => self.ram[address & 0x07ff] = value, 0x2000...0x3fff => self.ppu.cpuWrite( &self.cartridge, address & 7, value, ), 0x4000...0x4013 => self.apu.write( address, value, ), // OAMDMA 0x4014 => self.oam_dma_page = value, 0x4015 => self.apu.writeStatus(value), 0x4016 => { self.controllers[0].write(value); self.controllers[1].write(value); }, 0x4017 => self.apu.writeFrameCounter(value), else => self.cartridge.cpuWrite( address, value, ), } } /// Called exactly once for every CPU cycle. pub inline fn tick( self: *Bus, ) void { self.apu.tick(); // NTSC NES: PPU runs at 3× CPU clock. self.ppu.tick(&self.cartridge); self.ppu.tick(&self.cartridge); self.ppu.tick(&self.cartridge); } pub fn nmiLine( self: *Bus, ) bool { return self.ppu.nmiAsserted(); } pub fn irqLine( self: *Bus, ) bool { return self.apu.irqAsserted() or self.cartridge.irqAsserted(); } pub fn takeOamDmaPage( self: *Bus, ) ?u8 { const page = self.oam_dma_page; self.oam_dma_page = null; return page; }