feat(cpu): implement HALT

This commit is contained in:
Rekai Nyangadzayi Musuka 2023-11-04 01:21:52 -05:00
parent 16b3325fe3
commit 83f94f6d9d
4 changed files with 52 additions and 15 deletions

View File

@ -41,6 +41,10 @@ pub fn reset(self: *@This()) void {
self.tick = 0;
}
pub inline fn peekTimestamp(self: *const @This()) u64 {
return self.queue.items[0].tick;
}
pub inline fn check(self: *@This()) ?Event {
@setRuntimeSafety(false);
if (self.tick < self.queue.items[0].tick) return null;

View File

@ -107,9 +107,18 @@ pub fn runFrame(scheduler: *Scheduler, system: System) void {
const frame_end = scheduler.tick + cycles_per_frame;
while (scheduler.tick < frame_end) {
system.arm7tdmi.step();
system.arm946es.step();
system.arm946es.step();
switch (isHalted(system)) {
.both => scheduler.tick = scheduler.peekTimestamp(),
inline else => |halt| {
if (comptime halt != .arm9) {
system.arm946es.step();
system.arm946es.step();
}
if (comptime halt != .arm7)
system.arm7tdmi.step();
},
}
if (scheduler.check()) |ev| {
const late = scheduler.tick - ev.tick;
@ -126,6 +135,18 @@ pub fn runFrame(scheduler: *Scheduler, system: System) void {
}
}
const Halted = enum { arm7, arm9, both, none };
inline fn isHalted(system: System) Halted {
const ret = [_]Halted{ .none, .arm7, .arm9, .both };
const nds7_bus: *System.Bus7 = @ptrCast(@alignCast(system.arm7tdmi.bus.ptr));
const nds9_halt: u2 = @intFromBool(system.cp15.wait_for_interrupt);
const nds7_halt: u2 = @intFromBool(nds7_bus.io.haltcnt == .halt);
return ret[(nds9_halt << 1) | nds7_halt];
}
// FIXME: Perf win to allocating on the stack instead?
pub const SharedCtx = struct {
const MiB = 0x100000;
@ -320,8 +341,13 @@ pub fn handleInterrupt(comptime dev: Wram.Device, cpu: if (dev == .nds9) *System
if (!bus_ptr.io.ime or cpu.cpsr.i.read()) return; // ensure irqs are enabled
if ((bus_ptr.io.ie.raw & bus_ptr.io.irq.raw) == 0) return; // ensure there is an irq to handle
// TODO: Handle HALT
// HALTCNG (NDS7) and CP15 (NDS9)
switch (dev) {
.nds9 => {
const cp15: *System.Cp15 = @ptrCast(@alignCast(cpu.cp15.ptr));
cp15.wait_for_interrupt = false;
},
.nds7 => bus_ptr.io.haltcnt = .execute,
}
const ret_addr = cpu.r[15] - if (cpu.cpsr.t.read()) 0 else @as(u32, 4);
const spsr = cpu.cpsr;

View File

@ -15,28 +15,26 @@ const log = std.log.scoped(.nds7_io);
pub const Io = struct {
shr: *SharedCtx.Io,
/// Interrupt Master Enable
/// IME - Interrupt Master Enable
/// Read/Write
ime: bool = false,
/// Interrupt Enable
/// IE - Interrupt Enable
/// Read/Write
///
/// Caller must cast the `u32` to either `nds7.IntEnable` or `nds9.IntEnable`
ie: IntEnable = .{ .raw = 0x0000_0000 },
/// IF - Interrupt Request
/// Read/Write
///
/// Caller must cast the `u32` to either `nds7.IntRequest` or `nds9.IntRequest`
irq: IntRequest = .{ .raw = 0x0000_0000 },
/// Post Boot Flag
/// POSTFLG - Post Boot Flag
/// Read/Write
///
/// Caller must cast the `u8` to either `nds7.PostFlg` or `nds9.PostFlg`
postflg: PostFlag = .in_progress,
/// HALTCNT - Low Power Mode Control
/// Read/Write
haltcnt: Haltcnt = .execute,
pub fn init(io: *SharedCtx.Io) @This() {
return .{ .shr = io };
}
@ -140,4 +138,11 @@ pub const Vramstat = extern union {
raw: u8,
};
const Haltcnt = enum(u2) {
execute = 0,
gba_mode,
halt,
sleep,
};
const PostFlag = enum(u8) { in_progress = 0, completed };

View File

@ -8,6 +8,8 @@ control: u32 = 0x0005_2078,
dtcm_size_base: u32 = 0x0300_000A,
itcm_size_base: u32 = 0x0000_0020,
wait_for_interrupt: bool = false,
// Protection Unit
// cache_bits_data_unified: u32 = 0x0000_0000,
// cache_write_bufability: u32 = 0x0000_0000, // For Data Protection Regions
@ -73,7 +75,7 @@ pub fn write(self: *@This(), op1: u3, cn: u4, cm: u4, op2: u3, value: u32) void
0b000_0110_0111_001,
=> log.err("TODO: write to PU instruction region #{}", .{cm}),
0b000_0111_0000_100 => log.err("TODO: halt ARM946E-S", .{}),
0b000_0111_0000_100 => self.wait_for_interrupt = true, // NDS9 Halt
0b000_0111_0101_000 => log.err("TODO: invalidate instruction cache", .{}),
0b000_0111_0110_000 => log.err("TODO: invalidate data cache", .{}),
0b000_0111_1010_100 => log.err("TODO: drain write buffer", .{}),