From d985eac0fc9e6b707d857f16a509caac8b56c81a Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Thu, 23 Feb 2023 22:12:06 -0600 Subject: [PATCH] tmp: implement mechanisms for a emu reset fn (currently crashes) --- src/core/Bus.zig | 26 ++++++++++++++++++++++ src/core/apu.zig | 38 ++++++++++++++++++++++++++++---- src/core/apu/device/Envelope.zig | 9 ++++---- src/core/apu/device/Length.zig | 6 ++--- src/core/apu/device/Sweep.zig | 20 +++++------------ src/core/bus/Bios.zig | 4 ++++ src/core/bus/Ewram.zig | 4 ++++ src/core/bus/Iwram.zig | 4 ++++ src/core/bus/dma.zig | 4 ++++ src/core/bus/io.zig | 4 ++++ src/core/bus/timer.zig | 6 +++++ src/core/cpu.zig | 8 +++++++ src/core/emu.zig | 7 ++++++ src/core/scheduler.zig | 15 +++++++++++-- src/imgui.zig | 6 +++-- 15 files changed, 131 insertions(+), 30 deletions(-) diff --git a/src/core/Bus.zig b/src/core/Bus.zig index 29e9076..925f1ba 100644 --- a/src/core/Bus.zig +++ b/src/core/Bus.zig @@ -102,6 +102,32 @@ pub fn deinit(self: *Self) void { self.* = undefined; } +pub fn reset(self: *Self) void { + self.bios.reset(); + // TODO: deinit ppu + self.apu.reset(); + self.iwram.reset(); + self.ewram.reset(); + + // https://github.com/ziglang/zig/issues/14705 + { + comptime var i: usize = 0; + inline while (i < self.dma.len) : (i += 1) { + self.dma[0].reset(); + } + } + + // https://github.com/ziglang/zig/issues/14705 + { + comptime var i: usize = 0; + inline while (i < self.tim.len) : (i += 1) { + self.tim[0].reset(); + } + } + + self.io.reset(); +} + fn fillReadTable(self: *Self, table: *[table_len]?*const anyopaque) void { const vramMirror = @import("ppu/Vram.zig").mirror; diff --git a/src/core/apu.zig b/src/core/apu.zig index 05b6cea..8730290 100644 --- a/src/core/apu.zig +++ b/src/core/apu.zig @@ -289,7 +289,28 @@ pub const Apu = struct { return apu; } - fn reset(self: *Self) void { + /// Used when resetting the emulator + pub fn reset(self: *Self) void { + // FIXME: These reset functions are meant to emulate obscure APU behaviour. Write proper emu reset fns + self.ch1.reset(); + self.ch2.reset(); + self.ch3.reset(); + self.ch4.reset(); + + self.chA.reset(); + self.chB.reset(); + + self.psg_cnt = .{ .raw = 0 }; + self.dma_cnt = .{ .raw = 0 }; + self.cnt = .{ .raw = 0 }; + self.bias = .{ .raw = 0x200 }; + + self.sampling_cycle = 0; + self.fs.reset(); + } + + /// Emulates the reset behaviour of the APU + fn _reset(self: *Self) void { // All PSG Registers between 0x0400_0060..0x0400_0081 are zeroed // 0x0400_0082 and 0x0400_0088 retain their values self.ch1.reset(); @@ -351,7 +372,7 @@ pub const Apu = struct { // Rest Noise self.ch4.lfsr.reset(); } else { - self.reset(); + self._reset(); } } @@ -528,6 +549,11 @@ pub fn DmaSound(comptime kind: DmaSoundKind) type { }; } + /// Used when resetting hte emulator (not emulation code) + fn reset(self: *Self) void { + self.* = Self.init(); + } + pub fn push(self: *Self, value: u32) void { if (!self.enabled) self.enable(); @@ -562,10 +588,14 @@ pub const FrameSequencer = struct { const Self = @This(); pub const interval = (1 << 24) / 512; - step: u3, + step: u3 = 0, pub fn init() Self { - return .{ .step = 0 }; + return .{}; + } + + pub fn reset(self: *Self) void { + self.* = .{}; } pub fn tick(self: *Self) void { diff --git a/src/core/apu/device/Envelope.zig b/src/core/apu/device/Envelope.zig index 9eee8fe..49ea53c 100644 --- a/src/core/apu/device/Envelope.zig +++ b/src/core/apu/device/Envelope.zig @@ -3,17 +3,16 @@ const io = @import("../../bus/io.zig"); const Self = @This(); /// Period Timer -timer: u3, +timer: u3 = 0, /// Current Volume -vol: u4, +vol: u4 = 0, pub fn create() Self { - return .{ .timer = 0, .vol = 0 }; + return .{}; } pub fn reset(self: *Self) void { - self.timer = 0; - self.vol = 0; + self.* = .{}; } pub fn tick(self: *Self, nrx2: io.Envelope) void { diff --git a/src/core/apu/device/Length.zig b/src/core/apu/device/Length.zig index 420daf0..8478188 100644 --- a/src/core/apu/device/Length.zig +++ b/src/core/apu/device/Length.zig @@ -1,13 +1,13 @@ const Self = @This(); -timer: u9, +timer: u9 = 0, pub fn create() Self { - return .{ .timer = 0 }; + return .{}; } pub fn reset(self: *Self) void { - self.timer = 0; + self.* = .{}; } pub fn tick(self: *Self, enabled: bool, ch_enable: *bool) void { diff --git a/src/core/apu/device/Sweep.zig b/src/core/apu/device/Sweep.zig index 605697d..82877e8 100644 --- a/src/core/apu/device/Sweep.zig +++ b/src/core/apu/device/Sweep.zig @@ -3,26 +3,18 @@ const ToneSweep = @import("../ToneSweep.zig"); const Self = @This(); -timer: u8, -enabled: bool, -shadow: u11, +timer: u8 = 0, +enabled: bool = false, +shadow: u11 = 0, -calc_performed: bool, +calc_performed: bool = false, pub fn create() Self { - return .{ - .timer = 0, - .enabled = false, - .shadow = 0, - .calc_performed = false, - }; + return .{}; } pub fn reset(self: *Self) void { - self.timer = 0; - self.enabled = false; - self.shadow = 0; - self.calc_performed = false; + self.* = .{}; } pub fn tick(self: *Self, ch1: *ToneSweep) void { diff --git a/src/core/bus/Bios.zig b/src/core/bus/Bios.zig index 84e6e7d..6743ffc 100644 --- a/src/core/bus/Bios.zig +++ b/src/core/bus/Bios.zig @@ -77,6 +77,10 @@ pub fn init(allocator: Allocator, maybe_path: ?[]const u8) !Self { return Self{ .buf = buf, .allocator = allocator }; } +pub fn reset(self: *Self) void { + self.addr_latch = 0; +} + pub fn deinit(self: *Self) void { if (self.buf) |buf| self.allocator.free(buf); self.* = undefined; diff --git a/src/core/bus/Ewram.zig b/src/core/bus/Ewram.zig index c36d998..a1ad801 100644 --- a/src/core/bus/Ewram.zig +++ b/src/core/bus/Ewram.zig @@ -35,6 +35,10 @@ pub fn init(allocator: Allocator) !Self { }; } +pub fn reset(self: *Self) void { + std.mem.set(u8, self.buf, 0); +} + pub fn deinit(self: *Self) void { self.allocator.free(self.buf); self.* = undefined; diff --git a/src/core/bus/Iwram.zig b/src/core/bus/Iwram.zig index 383075e..0356460 100644 --- a/src/core/bus/Iwram.zig +++ b/src/core/bus/Iwram.zig @@ -35,6 +35,10 @@ pub fn init(allocator: Allocator) !Self { }; } +pub fn reset(self: *Self) void { + std.mem.set(u8, self.buf, 0); +} + pub fn deinit(self: *Self) void { self.allocator.free(self.buf); self.* = undefined; diff --git a/src/core/bus/dma.zig b/src/core/bus/dma.zig index e1f1b1c..7d75d18 100644 --- a/src/core/bus/dma.zig +++ b/src/core/bus/dma.zig @@ -195,6 +195,10 @@ fn DmaController(comptime id: u2) type { }; } + pub fn reset(self: *Self) void { + self.* = Self.init(); + } + pub fn setDmasad(self: *Self, addr: u32) void { self.sad = addr & sad_mask; } diff --git a/src/core/bus/io.zig b/src/core/bus/io.zig index 3b62d1f..2cbbb32 100644 --- a/src/core/bus/io.zig +++ b/src/core/bus/io.zig @@ -38,6 +38,10 @@ pub const Io = struct { }; } + pub fn reset(self: *Self) void { + self.* = Self.init(); + } + fn setIrqs(self: *Io, word: u32) void { self.ie.raw = @truncate(u16, word); self.irq.raw &= ~@truncate(u16, word >> 16); diff --git a/src/core/bus/timer.zig b/src/core/bus/timer.zig index ad7b5f0..93ff7c4 100644 --- a/src/core/bus/timer.zig +++ b/src/core/bus/timer.zig @@ -128,6 +128,12 @@ fn Timer(comptime id: u2) type { }; } + pub fn reset(self: *Self) void { + const scheduler = self.sched; + + self.* = Self.init(scheduler); + } + /// TIMCNT_L Getter pub fn timcntL(self: *const Self) u16 { if (self.cnt.cascade.read() or !self.cnt.enabled.read()) return self._counter; diff --git a/src/core/cpu.zig b/src/core/cpu.zig index 669d195..e005c9e 100644 --- a/src/core/cpu.zig +++ b/src/core/cpu.zig @@ -314,6 +314,14 @@ pub const Arm7tdmi = struct { }; } + // FIXME: Resetting disables logging (if enabled) + pub fn reset(self: *Self) void { + const bus_ptr = self.bus; + const scheduler_ptr = self.sched; + + self.* = Self.init(scheduler_ptr, bus_ptr, null); + } + pub inline fn hasSPSR(self: *const Self) bool { const mode = getModeChecked(self, self.cpsr.mode.read()); return switch (mode) { diff --git a/src/core/emu.zig b/src/core/emu.zig index 11931d7..215173f 100644 --- a/src/core/emu.zig +++ b/src/core/emu.zig @@ -222,3 +222,10 @@ pub const EmuThing = struct { } } }; + +pub fn reset(cpu: *Arm7tdmi) void { + // @breakpoint(); + cpu.sched.reset(); // Yes this is order sensitive, see the PPU reset for why + cpu.bus.reset(); + cpu.reset(); +} diff --git a/src/core/scheduler.zig b/src/core/scheduler.zig index 8a57183..86ebab1 100644 --- a/src/core/scheduler.zig +++ b/src/core/scheduler.zig @@ -11,11 +11,11 @@ const log = std.log.scoped(.Scheduler); pub const Scheduler = struct { const Self = @This(); - tick: u64, + tick: u64 = 0, queue: PriorityQueue(Event, void, lessThan), pub fn init(allocator: Allocator) Self { - var sched = Self{ .tick = 0, .queue = PriorityQueue(Event, void, lessThan).init(allocator, {}) }; + var sched = Self{ .queue = PriorityQueue(Event, void, lessThan).init(allocator, {}) }; sched.queue.add(.{ .kind = .HeatDeath, .tick = std.math.maxInt(u64) }) catch unreachable; return sched; @@ -26,6 +26,17 @@ pub const Scheduler = struct { self.* = undefined; } + pub fn reset(self: *Self) void { + // `std.PriorityQueue` provides no reset function, so we will just create a new one + const allocator = self.queue.allocator; + self.queue.deinit(); + + var new_queue = PriorityQueue(Event, void, lessThan).init(allocator, {}); + new_queue.add(.{ .kind = .HeatDeath, .tick = std.math.maxInt(u64) }) catch unreachable; + + self.* = .{ .queue = new_queue }; + } + pub inline fn now(self: *const Self) u64 { return self.tick; } diff --git a/src/imgui.zig b/src/imgui.zig index 2dcf60f..1d62181 100644 --- a/src/imgui.zig +++ b/src/imgui.zig @@ -48,7 +48,7 @@ pub const State = struct { } }; -pub fn draw(state: *State, tex_id: GLuint, cpu: *const Arm7tdmi) void { +pub fn draw(state: *State, tex_id: GLuint, cpu: *Arm7tdmi) void { const win_scale = config.config().host.win_scale; { @@ -77,7 +77,9 @@ pub fn draw(state: *State, tex_id: GLuint, cpu: *const Arm7tdmi) void { if (zgui.beginMenu("Emulation", true)) { defer zgui.endMenu(); - if (zgui.menuItem("Restart", .{})) log.warn("TODO: Restart Emulator", .{}); + if (zgui.menuItem("Restart", .{})) { + emu.reset(cpu); + } } }