feat(ppu): implement vblank, hblank and coincidence interrupts
This commit is contained in:
parent
bf08e93508
commit
37aff56c22
|
@ -1,7 +1,6 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
const Bus9 = @import("nds9/Bus.zig");
|
const System = @import("emu.zig").System;
|
||||||
const Bus7 = @import("nds7/Bus.zig");
|
|
||||||
|
|
||||||
const PriorityQueue = std.PriorityQueue(Event, void, Event.lessThan);
|
const PriorityQueue = std.PriorityQueue(Event, void, Event.lessThan);
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
|
@ -52,25 +51,25 @@ pub inline fn check(self: *@This()) ?Event {
|
||||||
return self.queue.remove();
|
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) {
|
switch (event.kind) {
|
||||||
.heat_death => unreachable,
|
.heat_death => unreachable,
|
||||||
.nds7 => |ev| {
|
.nds7 => |ev| {
|
||||||
const bus: *Bus7 = @ptrCast(@alignCast(bus_ptr));
|
const bus = system.bus7;
|
||||||
_ = bus;
|
_ = bus;
|
||||||
|
|
||||||
switch (ev) {}
|
switch (ev) {}
|
||||||
},
|
},
|
||||||
.nds9 => |ev| {
|
.nds9 => |ev| {
|
||||||
const bus: *Bus9 = @ptrCast(@alignCast(bus_ptr));
|
const bus = system.bus9;
|
||||||
|
|
||||||
switch (ev) {
|
switch (ev) {
|
||||||
.draw => {
|
.draw => {
|
||||||
bus.ppu.drawScanline(bus);
|
bus.ppu.drawScanline(bus);
|
||||||
bus.ppu.onHdrawEnd(self, late);
|
bus.ppu.onHdrawEnd(system, self, late);
|
||||||
},
|
},
|
||||||
.hblank => bus.ppu.onHblankEnd(self, late),
|
.hblank => bus.ppu.onHblankEnd(system, self, late),
|
||||||
.vblank => bus.ppu.onVblankEnd(self, late),
|
.vblank => bus.ppu.onVblankEnd(system, self, late),
|
||||||
.sqrt => bus.io.sqrt.onSqrtCalc(),
|
.sqrt => bus.io.sqrt.onSqrtCalc(),
|
||||||
.div => bus.io.div.onDivCalc(),
|
.div => bus.io.div.onDivCalc(),
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,8 +129,9 @@ pub fn runFrame(scheduler: *Scheduler, system: System) void {
|
||||||
.nds7 => system.bus7,
|
.nds7 => system.bus7,
|
||||||
.nds9 => system.bus9,
|
.nds9 => system.bus9,
|
||||||
};
|
};
|
||||||
|
_ = bus_ptr;
|
||||||
|
|
||||||
scheduler.handle(bus_ptr, ev, late);
|
scheduler.handle(system, ev, late);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -320,6 +320,10 @@ pub const masks = struct {
|
||||||
|
|
||||||
// FIXME: bitfields depends on NDS9 / NDS7
|
// FIXME: bitfields depends on NDS9 / NDS7
|
||||||
pub const IntEnable = extern union {
|
pub const IntEnable = extern union {
|
||||||
|
vblank: Bit(u32, 0),
|
||||||
|
hblank: Bit(u32, 1),
|
||||||
|
coincidence: Bit(u32, 2),
|
||||||
|
|
||||||
ipcsync: Bit(u32, 16),
|
ipcsync: Bit(u32, 16),
|
||||||
ipc_send_empty: Bit(u32, 17),
|
ipc_send_empty: Bit(u32, 17),
|
||||||
ipc_recv_not_empty: Bit(u32, 18),
|
ipc_recv_not_empty: Bit(u32, 18),
|
||||||
|
|
|
@ -8,6 +8,8 @@ const Vram = @import("ppu/Vram.zig");
|
||||||
const EngineA = @import("ppu/engine.zig").EngineA;
|
const EngineA = @import("ppu/engine.zig").EngineA;
|
||||||
const EngineB = @import("ppu/engine.zig").EngineB;
|
const EngineB = @import("ppu/engine.zig").EngineB;
|
||||||
|
|
||||||
|
const handleInterrupt = @import("emu.zig").handleInterrupt;
|
||||||
|
|
||||||
pub const screen_width = 256;
|
pub const screen_width = 256;
|
||||||
pub const screen_height = 192;
|
pub const screen_height = 192;
|
||||||
const KiB = 0x400;
|
const KiB = 0x400;
|
||||||
|
@ -59,35 +61,49 @@ pub const Ppu = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// HDraw -> HBlank
|
/// HDraw -> HBlank
|
||||||
pub fn onHdrawEnd(self: *@This(), scheduler: *Scheduler, late: u64) void {
|
pub fn onHdrawEnd(self: *@This(), system: System, scheduler: *Scheduler, late: u64) void {
|
||||||
std.debug.assert(self.io.nds9.dispstat.hblank.read() == false);
|
if (self.io.nds9.dispstat.hblank_irq.read()) {
|
||||||
std.debug.assert(self.io.nds9.dispstat.vblank.read() == false);
|
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.nds9.dispstat.hblank.set();
|
||||||
self.io.nds7.dispstat.hblank.set();
|
self.io.nds7.dispstat.hblank.set();
|
||||||
|
|
||||||
// TODO: Signal HBlank IRQ
|
|
||||||
|
|
||||||
const dots_in_hblank = 99;
|
const dots_in_hblank = 99;
|
||||||
scheduler.push(.{ .nds9 = .hblank }, dots_in_hblank * cycles_per_dot -| late);
|
scheduler.push(.{ .nds9 = .hblank }, dots_in_hblank * cycles_per_dot -| late);
|
||||||
}
|
}
|
||||||
|
|
||||||
// VBlank -> HBlank (Still VBlank)
|
/// VBlank -> HBlank (Still VBlank)
|
||||||
pub fn onVblankEnd(self: *@This(), scheduler: *Scheduler, late: u64) void {
|
pub fn onVblankEnd(self: *@This(), system: System, scheduler: *Scheduler, late: u64) void {
|
||||||
std.debug.assert(!self.io.nds9.dispstat.hblank.read());
|
if (self.io.nds9.dispstat.hblank_irq.read()) {
|
||||||
std.debug.assert(self.io.nds9.vcount.scanline.read() == 262 or self.io.nds9.dispstat.vblank.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.nds9.dispstat.hblank.set();
|
||||||
self.io.nds7.dispstat.hblank.set();
|
self.io.nds7.dispstat.hblank.set();
|
||||||
|
|
||||||
// TODO: Signal HBlank IRQ
|
// TODO: Run DMAs on HBlank
|
||||||
|
|
||||||
const dots_in_hblank = 99;
|
const dots_in_hblank = 99;
|
||||||
scheduler.push(.{ .nds9 = .hblank }, dots_in_hblank * cycles_per_dot -| late);
|
scheduler.push(.{ .nds9 = .hblank }, dots_in_hblank * cycles_per_dot -| late);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// HBlank -> HDraw / VBlank
|
/// 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 scanline_total = 263; // 192 visible, 71 blanking
|
||||||
|
|
||||||
const prev_scanline = self.io.nds9.vcount.scanline.read();
|
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();
|
const coincidence = scanline == self.io.nds9.dispstat.lyc.read();
|
||||||
self.io.nds9.dispstat.coincidence.write(coincidence);
|
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();
|
const coincidence = scanline == self.io.nds7.dispstat.lyc.read();
|
||||||
self.io.nds7.dispstat.coincidence.write(coincidence);
|
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) {
|
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
|
// Draw Another Scanline
|
||||||
const dots_in_hdraw = 256;
|
const dots_in_hdraw = 256;
|
||||||
return scheduler.push(.{ .nds9 = .draw }, dots_in_hdraw * cycles_per_dot -| late);
|
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
|
// Transition from Hblank to Vblank
|
||||||
self.fb.swap();
|
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.nds9.dispstat.vblank.set();
|
||||||
self.io.nds7.dispstat.vblank.set();
|
self.io.nds7.dispstat.vblank.set();
|
||||||
|
|
||||||
// TODO: Signal VBlank IRQ
|
// TODO: Affine BG Latches
|
||||||
|
// TODO: VBlank DMA Transfers
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scanline == 262) {
|
if (scanline == 262) {
|
||||||
self.io.nds9.dispstat.vblank.unset();
|
self.io.nds9.dispstat.vblank.unset();
|
||||||
self.io.nds7.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;
|
const dots_in_vblank = 256;
|
||||||
|
|
Loading…
Reference in New Issue