158 lines
2.8 KiB
Zig
158 lines
2.8 KiB
Zig
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;
|
||
}
|