From 37aff56c2201aa48bf68b62d3974767f79725d8c Mon Sep 17 00:00:00 2001 From: paoda Date: Sat, 4 Nov 2023 03:22:11 -0500 Subject: [PATCH] feat(ppu): implement vblank, hblank and coincidence interrupts --- src/core/Scheduler.zig | 15 +++++---- src/core/emu.zig | 3 +- src/core/io.zig | 4 +++ src/core/ppu.zig | 69 ++++++++++++++++++++++++++++++------------ 4 files changed, 63 insertions(+), 28 deletions(-) diff --git a/src/core/Scheduler.zig b/src/core/Scheduler.zig index a9273d8..cc65c5a 100644 --- a/src/core/Scheduler.zig +++ b/src/core/Scheduler.zig @@ -1,7 +1,6 @@ const std = @import("std"); -const Bus9 = @import("nds9/Bus.zig"); -const Bus7 = @import("nds7/Bus.zig"); +const System = @import("emu.zig").System; const PriorityQueue = std.PriorityQueue(Event, void, Event.lessThan); const Allocator = std.mem.Allocator; @@ -52,25 +51,25 @@ pub inline fn check(self: *@This()) ?Event { return self.queue.remove(); } -pub fn handle(self: *@This(), bus_ptr: ?*anyopaque, event: Event, late: u64) void { +pub fn handle(self: *@This(), system: System, event: Event, late: u64) void { switch (event.kind) { .heat_death => unreachable, .nds7 => |ev| { - const bus: *Bus7 = @ptrCast(@alignCast(bus_ptr)); + const bus = system.bus7; _ = bus; switch (ev) {} }, .nds9 => |ev| { - const bus: *Bus9 = @ptrCast(@alignCast(bus_ptr)); + const bus = system.bus9; switch (ev) { .draw => { bus.ppu.drawScanline(bus); - bus.ppu.onHdrawEnd(self, late); + bus.ppu.onHdrawEnd(system, self, late); }, - .hblank => bus.ppu.onHblankEnd(self, late), - .vblank => bus.ppu.onVblankEnd(self, late), + .hblank => bus.ppu.onHblankEnd(system, self, late), + .vblank => bus.ppu.onVblankEnd(system, self, late), .sqrt => bus.io.sqrt.onSqrtCalc(), .div => bus.io.div.onDivCalc(), } diff --git a/src/core/emu.zig b/src/core/emu.zig index 460ecb0..abb878c 100644 --- a/src/core/emu.zig +++ b/src/core/emu.zig @@ -129,8 +129,9 @@ pub fn runFrame(scheduler: *Scheduler, system: System) void { .nds7 => system.bus7, .nds9 => system.bus9, }; + _ = bus_ptr; - scheduler.handle(bus_ptr, ev, late); + scheduler.handle(system, ev, late); } } } diff --git a/src/core/io.zig b/src/core/io.zig index eee796b..788f3d7 100644 --- a/src/core/io.zig +++ b/src/core/io.zig @@ -320,6 +320,10 @@ pub const masks = struct { // FIXME: bitfields depends on NDS9 / NDS7 pub const IntEnable = extern union { + vblank: Bit(u32, 0), + hblank: Bit(u32, 1), + coincidence: Bit(u32, 2), + ipcsync: Bit(u32, 16), ipc_send_empty: Bit(u32, 17), ipc_recv_not_empty: Bit(u32, 18), diff --git a/src/core/ppu.zig b/src/core/ppu.zig index c85d1c1..0479585 100644 --- a/src/core/ppu.zig +++ b/src/core/ppu.zig @@ -8,6 +8,8 @@ const Vram = @import("ppu/Vram.zig"); const EngineA = @import("ppu/engine.zig").EngineA; const EngineB = @import("ppu/engine.zig").EngineB; +const handleInterrupt = @import("emu.zig").handleInterrupt; + pub const screen_width = 256; pub const screen_height = 192; const KiB = 0x400; @@ -59,35 +61,49 @@ pub const Ppu = struct { } /// HDraw -> HBlank - pub fn onHdrawEnd(self: *@This(), scheduler: *Scheduler, late: u64) void { - std.debug.assert(self.io.nds9.dispstat.hblank.read() == false); - std.debug.assert(self.io.nds9.dispstat.vblank.read() == false); + pub fn onHdrawEnd(self: *@This(), system: System, scheduler: *Scheduler, late: u64) void { + if (self.io.nds9.dispstat.hblank_irq.read()) { + system.bus9.io.irq.hblank.set(); + handleInterrupt(.nds9, system.arm946es); + } + + if (self.io.nds7.dispstat.hblank_irq.read()) { + system.bus7.io.irq.hblank.set(); + handleInterrupt(.nds7, system.arm7tdmi); + } + + // TODO: Run DMAs on HBlank self.io.nds9.dispstat.hblank.set(); self.io.nds7.dispstat.hblank.set(); - // TODO: Signal HBlank IRQ - const dots_in_hblank = 99; scheduler.push(.{ .nds9 = .hblank }, dots_in_hblank * cycles_per_dot -| late); } - // VBlank -> HBlank (Still VBlank) - pub fn onVblankEnd(self: *@This(), scheduler: *Scheduler, late: u64) void { - std.debug.assert(!self.io.nds9.dispstat.hblank.read()); - std.debug.assert(self.io.nds9.vcount.scanline.read() == 262 or self.io.nds9.dispstat.vblank.read()); + /// VBlank -> HBlank (Still VBlank) + pub fn onVblankEnd(self: *@This(), system: System, scheduler: *Scheduler, late: u64) void { + if (self.io.nds9.dispstat.hblank_irq.read()) { + system.bus9.io.irq.hblank.set(); + handleInterrupt(.nds9, system.arm946es); + } + + if (self.io.nds7.dispstat.hblank_irq.read()) { + system.bus7.io.irq.hblank.set(); + handleInterrupt(.nds7, system.arm7tdmi); + } self.io.nds9.dispstat.hblank.set(); self.io.nds7.dispstat.hblank.set(); - // TODO: Signal HBlank IRQ + // TODO: Run DMAs on HBlank const dots_in_hblank = 99; scheduler.push(.{ .nds9 = .hblank }, dots_in_hblank * cycles_per_dot -| late); } /// HBlank -> HDraw / VBlank - pub fn onHblankEnd(self: *@This(), scheduler: *Scheduler, late: u64) void { + pub fn onHblankEnd(self: *@This(), system: System, scheduler: *Scheduler, late: u64) void { const scanline_total = 263; // 192 visible, 71 blanking const prev_scanline = self.io.nds9.vcount.scanline.read(); @@ -102,18 +118,24 @@ pub const Ppu = struct { { const coincidence = scanline == self.io.nds9.dispstat.lyc.read(); self.io.nds9.dispstat.coincidence.write(coincidence); + + if (coincidence and self.io.nds9.dispstat.vcount_irq.read()) { + system.bus9.io.irq.coincidence.set(); + handleInterrupt(.nds9, system.arm946es); + } } + { const coincidence = scanline == self.io.nds7.dispstat.lyc.read(); self.io.nds7.dispstat.coincidence.write(coincidence); + + if (coincidence and self.io.nds7.dispstat.vcount_irq.read()) { + system.bus7.io.irq.coincidence.set(); + handleInterrupt(.nds7, system.arm7tdmi); + } } - // TODO: LYC == LY IRQ - if (scanline < 192) { - std.debug.assert(self.io.nds9.dispstat.vblank.read() == false); - std.debug.assert(self.io.nds9.dispstat.hblank.read() == false); - // Draw Another Scanline const dots_in_hdraw = 256; return scheduler.push(.{ .nds9 = .draw }, dots_in_hdraw * cycles_per_dot -| late); @@ -123,17 +145,26 @@ pub const Ppu = struct { // Transition from Hblank to Vblank self.fb.swap(); + if (self.io.nds9.dispstat.vblank_irq.read()) { + system.bus9.io.irq.vblank.set(); + handleInterrupt(.nds9, system.arm946es); + } + + if (self.io.nds7.dispstat.vblank_irq.read()) { + system.bus7.io.irq.vblank.set(); + handleInterrupt(.nds7, system.arm7tdmi); + } + self.io.nds9.dispstat.vblank.set(); self.io.nds7.dispstat.vblank.set(); - // TODO: Signal VBlank IRQ + // TODO: Affine BG Latches + // TODO: VBlank DMA Transfers } if (scanline == 262) { self.io.nds9.dispstat.vblank.unset(); self.io.nds7.dispstat.vblank.unset(); - - std.debug.assert(self.io.nds9.dispstat.vblank.read() == (scanline != 262)); } const dots_in_vblank = 256;