diff --git a/src/core/nds9/Scheduler.zig b/src/core/Scheduler.zig similarity index 54% rename from src/core/nds9/Scheduler.zig rename to src/core/Scheduler.zig index f01f6af..f317ae2 100644 --- a/src/core/nds9/Scheduler.zig +++ b/src/core/Scheduler.zig @@ -1,6 +1,7 @@ const std = @import("std"); -const Bus = @import("Bus.zig"); +const nds9 = @import("nds9.zig"); +const nds7 = @import("nds7.zig"); const PriorityQueue = std.PriorityQueue(Event, void, Event.lessThan); const Allocator = std.mem.Allocator; @@ -19,6 +20,12 @@ pub fn push(self: *@This(), kind: Event.Kind, offset: u64) void { self.queue.add(.{ .kind = kind, .tick = self.tick + offset }) catch unreachable; } +pub inline fn peek(self: *const @This()) Event { + @setRuntimeSafety(false); + + return self.queue.items[0]; +} + pub fn deinit(self: @This()) void { self.queue.deinit(); } @@ -37,18 +44,33 @@ pub fn reset(self: *@This()) void { self.tick = 0; } -pub fn handle(self: *@This(), bus: *Bus) void { +pub fn handle7(self: *@This(), bus: *nds7.Bus) void { + _ = bus; + const event = self.queue.remove(); + // const late = self.tick - event.tick; + + switch (event.kind) { + .nds7 => {}, + .heat_death => unreachable, + .nds9 => unreachable, // handled by handle7 + } +} + +pub fn handle9(self: *@This(), bus: *nds9.Bus) void { const event = self.queue.remove(); const late = self.tick - event.tick; switch (event.kind) { - .heat_death => unreachable, - .draw => { - bus.ppu.drawScanline(bus); - bus.ppu.onHdrawEnd(self, late); + .nds9 => |ev| switch (ev) { + .draw => { + bus.ppu.drawScanline(bus); + bus.ppu.onHdrawEnd(self, late); + }, + .hblank => bus.ppu.onHblankEnd(self, late), + .vblank => bus.ppu.onHblankEnd(self, late), }, - .hblank => bus.ppu.onHblankEnd(self, late), - .vblank => bus.ppu.onHblankEnd(self, late), + .heat_death => unreachable, + .nds7 => unreachable, // handled by handle7 } } @@ -56,13 +78,20 @@ pub const Event = struct { tick: u64, kind: Kind, - pub const Kind = enum { - heat_death, + pub const Event7 = enum {}; + + pub const Event9 = enum { draw, hblank, vblank, }; + pub const Kind = union(enum) { + heat_death: void, + nds9: Event9, + nds7: Event7, + }; + fn lessThan(_: void, left: @This(), right: @This()) std.math.Order { return std.math.order(left.tick, right.tick); } diff --git a/src/core/emu.zig b/src/core/emu.zig index 82c9461..9513fbb 100644 --- a/src/core/emu.zig +++ b/src/core/emu.zig @@ -4,6 +4,7 @@ const nds7 = @import("nds7.zig"); const Header = @import("cartridge.zig").Header; const SharedIo = @import("io.zig").Io; +const Scheduler = @import("Scheduler.zig"); const Arm946es = nds9.Arm946es; const Allocator = std.mem.Allocator; @@ -68,47 +69,83 @@ const dot_clock = 5585664; // 5.585664 Hz const arm7_clock = bus_clock; const arm9_clock = bus_clock * 2; -pub fn runFrame(nds7_group: nds7.Group, nds9_group: nds9.Group) void { - // TODO: might be more efficient to run them both in the same loop? - { - const scheduler = nds9_group.scheduler; - +pub fn runFrame(scheduler: *Scheduler, arm7: nds7.Group, arm9: nds9.Group) void { + const nds9_frame_end = blk: { const cycles_per_dot = arm9_clock / dot_clock + 1; comptime std.debug.assert(cycles_per_dot == 12); const cycles_per_frame = 355 * 263 * cycles_per_dot; - const frame_end = scheduler.tick + cycles_per_frame; + break :blk scheduler.tick + cycles_per_frame; + }; - const cpu = nds9_group.cpu; - const bus = nds9_group.bus; + // const nds7_frame_end = blk: { + // const cycles_per_dot = arm7_clock / dot_clock + 1; + // comptime std.debug.assert(cycles_per_dot == 6); - while (scheduler.tick < frame_end) { - cpu.step(); + // const cycles_per_frame = 355 * 263 * cycles_per_dot; + // const frame_end = scheduler.tick + cycles_per_frame; + // }; - if (scheduler.tick >= scheduler.next()) scheduler.handle(bus); - } - } + // TODO: Figure out Syncing - { - const scheduler = nds7_group.scheduler; + while (scheduler.tick < nds9_frame_end) { + arm7.cpu.step(); + arm9.cpu.step(); + arm9.cpu.step(); - const cycles_per_dot = arm7_clock / dot_clock + 1; - comptime std.debug.assert(cycles_per_dot == 6); + const next_ev = scheduler.peek(); - const cycles_per_frame = 355 * 263 * cycles_per_dot; - const frame_end = scheduler.tick + cycles_per_frame; - - const cpu = nds7_group.cpu; - const bus = nds7_group.bus; - - while (scheduler.tick < frame_end) { - cpu.step(); - - if (scheduler.tick >= scheduler.next()) scheduler.handle(bus); + if (scheduler.tick >= next_ev.tick) { + switch (next_ev.kind) { + .heat_death => unreachable, + .nds9 => scheduler.handle9(arm9.bus), + .nds7 => scheduler.handle7(arm7.bus), + } } } } +// pub fn runFrame(nds7_group: nds7.Group, nds9_group: nds9.Group) void { +// // TODO: might be more efficient to run them both in the same loop? +// { +// const scheduler = nds9_group.scheduler; + +// const cycles_per_dot = arm9_clock / dot_clock + 1; +// comptime std.debug.assert(cycles_per_dot == 12); + +// const cycles_per_frame = 355 * 263 * cycles_per_dot; +// const frame_end = scheduler.tick + cycles_per_frame; + +// const cpu = nds9_group.cpu; +// const bus = nds9_group.bus; + +// while (scheduler.tick < frame_end) { +// cpu.step(); + +// if (scheduler.tick >= scheduler.next()) scheduler.handle(bus); +// } +// } + +// { +// const scheduler = nds7_group.scheduler; + +// const cycles_per_dot = arm7_clock / dot_clock + 1; +// comptime std.debug.assert(cycles_per_dot == 6); + +// const cycles_per_frame = 355 * 263 * cycles_per_dot; +// const frame_end = scheduler.tick + cycles_per_frame; + +// const cpu = nds7_group.cpu; +// const bus = nds7_group.bus; + +// while (scheduler.tick < frame_end) { +// cpu.step(); + +// if (scheduler.tick >= scheduler.next()) scheduler.handle(bus); +// } +// } +// } + // FIXME: Perf win to allocating on the stack instead? pub const SharedContext = struct { const MiB = 0x100000; diff --git a/src/core/nds7.zig b/src/core/nds7.zig index fd1ab83..cddf4e5 100644 --- a/src/core/nds7.zig +++ b/src/core/nds7.zig @@ -2,7 +2,6 @@ const std = @import("std"); pub const Bus = @import("nds7/Bus.zig"); pub const io = @import("nds7/io.zig"); -pub const Scheduler = @import("nds7/Scheduler.zig"); pub const Arm7tdmi = @import("arm32").Arm7tdmi; const Allocator = std.mem.Allocator; @@ -11,11 +10,9 @@ const Allocator = std.mem.Allocator; pub const Group = struct { cpu: *Arm7tdmi, bus: *Bus, - scheduler: *Scheduler, /// Responsible for deallocated the ARM7 CPU, Bus and Scheduler pub fn deinit(self: @This(), allocator: Allocator) void { self.bus.deinit(allocator); - self.scheduler.deinit(); } }; diff --git a/src/core/nds7/Bus.zig b/src/core/nds7/Bus.zig index 69c681c..58fd941 100644 --- a/src/core/nds7/Bus.zig +++ b/src/core/nds7/Bus.zig @@ -1,7 +1,7 @@ const std = @import("std"); const io = @import("io.zig"); -const Scheduler = @import("Scheduler.zig"); +const Scheduler = @import("../Scheduler.zig"); const SharedIo = @import("../io.zig").Io; const SharedContext = @import("../emu.zig").SharedContext; const forceAlign = @import("../emu.zig").forceAlign; diff --git a/src/core/nds7/Scheduler.zig b/src/core/nds7/Scheduler.zig deleted file mode 100644 index 32e927a..0000000 --- a/src/core/nds7/Scheduler.zig +++ /dev/null @@ -1,59 +0,0 @@ -const std = @import("std"); - -const Bus = @import("Bus.zig"); - -const PriorityQueue = std.PriorityQueue(Event, void, Event.lessThan); -const Allocator = std.mem.Allocator; - -tick: u64 = 0, -queue: PriorityQueue, - -pub fn init(allocator: Allocator) !@This() { - var queue = PriorityQueue.init(allocator, {}); - try queue.add(.{ .tick = std.math.maxInt(u64), .kind = .heat_death }); - - return .{ .queue = queue }; -} - -pub fn push(self: *@This(), kind: Event.Kind, offset: u64) void { - self.queue.add(.{ .kind = kind, .tick = self.tick + offset }) catch unreachable; -} - -pub fn deinit(self: @This()) void { - self.queue.deinit(); -} - -pub fn now(self: @This()) u64 { - return self.tick; -} - -pub fn next(self: @This()) u64 { - @setRuntimeSafety(false); - return self.queue.items[0].tick; -} - -pub fn reset(self: *@This()) void { - self.tick = 0; -} - -pub fn handle(self: *@This(), bus: *Bus) void { - _ = bus; - const event = self.queue.remove(); - const late = self.tick - event.tick; - _ = late; - - switch (event.kind) { - .heat_death => unreachable, - } -} - -pub const Event = struct { - tick: u64, - kind: Kind, - - pub const Kind = enum { heat_death }; - - fn lessThan(_: void, left: @This(), right: @This()) std.math.Order { - return std.math.order(left.tick, right.tick); - } -}; diff --git a/src/core/nds9.zig b/src/core/nds9.zig index 6d6724a..d14c50a 100644 --- a/src/core/nds9.zig +++ b/src/core/nds9.zig @@ -3,7 +3,6 @@ const std = @import("std"); pub const Bus = @import("nds9/Bus.zig"); pub const Cp15 = @import("nds9/Cp15.zig"); pub const io = @import("nds9/io.zig"); -pub const Scheduler = @import("nds9/Scheduler.zig"); pub const Arm946es = @import("arm32").Arm946es; const Allocator = std.mem.Allocator; @@ -12,11 +11,9 @@ const Allocator = std.mem.Allocator; pub const Group = struct { cpu: *Arm946es, bus: *Bus, - scheduler: *Scheduler, /// Responsible for deallocating the ARM9 CPU, Bus and Scheduler pub fn deinit(self: @This(), allocator: Allocator) void { self.bus.deinit(allocator); - self.scheduler.deinit(); } }; diff --git a/src/core/nds9/Bus.zig b/src/core/nds9/Bus.zig index fdb2a67..19c3120 100644 --- a/src/core/nds9/Bus.zig +++ b/src/core/nds9/Bus.zig @@ -2,7 +2,7 @@ const std = @import("std"); const io = @import("io.zig"); const Ppu = @import("../ppu.zig").Ppu; -const Scheduler = @import("Scheduler.zig"); +const Scheduler = @import("../Scheduler.zig"); const SharedContext = @import("../emu.zig").SharedContext; const forceAlign = @import("../emu.zig").forceAlign; @@ -27,7 +27,7 @@ pub fn init(allocator: Allocator, scheduler: *Scheduler, shared_ctx: SharedConte @memset(vram1_mem, 0); const dots_per_cycle = 3; // ARM946E-S runs twice as fast as the ARM7TDMI - scheduler.push(.draw, 256 * dots_per_cycle); + scheduler.push(.{ .nds9 = .draw }, 256 * dots_per_cycle); return .{ .main = shared_ctx.main, diff --git a/src/core/ppu.zig b/src/core/ppu.zig index c0e8ba9..0d06369 100644 --- a/src/core/ppu.zig +++ b/src/core/ppu.zig @@ -2,6 +2,7 @@ const std = @import("std"); const Allocator = std.mem.Allocator; const nds9 = @import("nds9.zig"); +const Scheduler = @import("Scheduler.zig"); pub const screen_width = 256; pub const screen_height = 192; @@ -54,7 +55,7 @@ pub const Ppu = struct { } /// HDraw -> HBlank - pub fn onHdrawEnd(self: *@This(), nds9_scheduler: *nds9.Scheduler, late: u64) void { + pub fn onHdrawEnd(self: *@This(), scheduler: *Scheduler, late: u64) void { const dots_in_hblank = 99; std.debug.assert(self.io.dispstat.hblank.read() == false); std.debug.assert(self.io.dispstat.vblank.read() == false); @@ -62,10 +63,10 @@ pub const Ppu = struct { // TODO: Signal HBlank IRQ self.io.dispstat.hblank.set(); - nds9_scheduler.push(.hblank, dots_in_hblank * cycles_per_dot -| late); + scheduler.push(.{ .nds9 = .hblank }, dots_in_hblank * cycles_per_dot -| late); } - pub fn onHblankEnd(self: *@This(), nds9_scheduler: *nds9.Scheduler, late: u64) void { + pub fn onHblankEnd(self: *@This(), scheduler: *Scheduler, late: u64) void { const scanline_count = 192 + 71; const prev_scanline = self.io.vcount.scanline.read(); @@ -85,7 +86,7 @@ pub const Ppu = struct { // Draw Another Scanline const dots_in_hdraw = 256; - return nds9_scheduler.push(.draw, dots_in_hdraw * cycles_per_dot -| late); + return scheduler.push(.{ .nds9 = .draw }, dots_in_hdraw * cycles_per_dot -| late); } if (scanline == 192) { @@ -100,7 +101,7 @@ pub const Ppu = struct { std.debug.assert(self.io.dispstat.vblank.read() == (scanline != 262)); const dots_in_scanline = 256 + 99; - nds9_scheduler.push(.hblank, dots_in_scanline * cycles_per_dot -| late); + scheduler.push(.{ .nds9 = .hblank }, dots_in_scanline * cycles_per_dot -| late); } }; diff --git a/src/main.zig b/src/main.zig index e95c3b1..43c8d45 100644 --- a/src/main.zig +++ b/src/main.zig @@ -5,6 +5,8 @@ const nds9 = @import("core/nds9.zig"); const nds7 = @import("core/nds7.zig"); const emu = @import("core/emu.zig"); +const Scheduler = @import("core/Scheduler.zig"); + const IBus = @import("arm32").Bus; const IScheduler = @import("arm32").Scheduler; const ICoprocessor = @import("arm32").Coprocessor; @@ -41,23 +43,24 @@ pub fn main() !void { const shared_ctx = try SharedContext.init(allocator); defer shared_ctx.deinit(allocator); + var scheduler = try Scheduler.init(allocator); + defer scheduler.deinit(); + const nds9_group: nds9.Group = blk: { - var scheduler = try nds9.Scheduler.init(allocator); var bus = try nds9.Bus.init(allocator, &scheduler, shared_ctx); var cp15 = nds9.Cp15{}; var arm946es = nds9.Arm946es.init(IScheduler.init(&scheduler), IBus.init(&bus), ICoprocessor.init(&cp15)); - break :blk .{ .cpu = &arm946es, .bus = &bus, .scheduler = &scheduler }; + break :blk .{ .cpu = &arm946es, .bus = &bus }; }; defer nds9_group.deinit(allocator); const nds7_group: nds7.Group = blk: { - var scheduler = try nds7.Scheduler.init(allocator); var bus = try nds7.Bus.init(allocator, &scheduler, shared_ctx); var arm7tdmi = nds7.Arm7tdmi.init(IScheduler.init(&scheduler), IBus.init(&bus)); - break :blk .{ .cpu = &arm7tdmi, .bus = &bus, .scheduler = &scheduler }; + break :blk .{ .cpu = &arm7tdmi, .bus = &bus }; }; defer nds7_group.deinit(allocator); @@ -67,7 +70,7 @@ pub fn main() !void { defer ui.deinit(allocator); ui.setTitle(rom_title); - try ui.run(nds7_group, nds9_group); + try ui.run(&scheduler, nds7_group, nds9_group); } fn handlePositional(result: ClapResult) ![]const u8 { diff --git a/src/platform.zig b/src/platform.zig index f9f5acd..9d4fc44 100644 --- a/src/platform.zig +++ b/src/platform.zig @@ -11,6 +11,7 @@ const imgui = @import("ui/imgui.zig"); const emu = @import("core/emu.zig"); const KeyInput = @import("core/nds9/io.zig").KeyInput; +const Scheduler = @import("core/Scheduler.zig"); const Allocator = std.mem.Allocator; @@ -86,7 +87,7 @@ pub const Ui = struct { return SDL.SDL_GL_GetProcAddress(proc.ptr); } - pub fn run(self: *Self, nds7_group: nds7.Group, nds9_group: nds9.Group) !void { + pub fn run(self: *Self, scheduler: *Scheduler, nds7_group: nds7.Group, nds9_group: nds9.Group) !void { // TODO: Sort this out please const vao_id = opengl_impl.vao(); @@ -108,7 +109,7 @@ pub const Ui = struct { var event: SDL.SDL_Event = undefined; emu_loop: while (true) { - emu.runFrame(nds7_group, nds9_group); + emu.runFrame(scheduler, nds7_group, nds9_group); while (SDL.SDL_PollEvent(&event) != 0) { _ = zgui.backend.processEvent(&event);