const std = @import("std"); const Architecture = enum { v4t, v5te }; const Interpreter = @import("lib.zig").Interpreter; const Bus = @import("lib.zig").Bus; const Scheduler = @import("lib.zig").Scheduler; const Bitfield = @import("bitfield").Bitfield; const Bit = @import("bitfield").Bit; const condition_lut = [_]u16{ 0xF0F0, // EQ - Equal 0x0F0F, // NE - Not Equal 0xCCCC, // CS - Unsigned higher or same 0x3333, // CC - Unsigned lower 0xFF00, // MI - Negative 0x00FF, // PL - Positive or Zero 0xAAAA, // VS - Overflow 0x5555, // VC - No Overflow 0x0C0C, // HI - unsigned hierh 0xF3F3, // LS - unsigned lower or same 0xAA55, // GE - greater or equal 0x55AA, // LT - less than 0x0A05, // GT - greater than 0xF5FA, // LE - less than or equal 0xFFFF, // AL - always 0x0000, // NV - never }; pub fn Arm32(comptime arch: Architecture) type { return struct { const Self = @This(); r: [16]u32 = [_]u32{0x00} ** 16, pipe: Pipeline = Pipeline.init(), sched: Scheduler, bus: Bus, cpsr: PSR, spsr: PSR, bank: Bank = Bank.create(), const arm = @import("arm/v4t.zig").arm(Self); const thumb = @import("arm/v4t.zig").thumb(Self); const Pipeline = struct { stage: [2]?u32, flushed: bool, fn init() @This() { return .{ .stage = [_]?u32{null} ** 2, .flushed = false, }; } pub fn isFull(self: *const @This()) bool { return self.stage[0] != null and self.stage[1] != null; } pub fn step(self: *@This(), cpu: *Self, comptime T: type) ?u32 { comptime std.debug.assert(T == u32 or T == u16); const opcode = self.stage[0]; self.stage[0] = self.stage[1]; self.stage[1] = cpu.fetch(T, cpu.r[15]); return opcode; } pub fn reload(self: *@This(), cpu: *Self) void { if (cpu.cpsr.t.read()) { self.stage[0] = cpu.fetch(u16, cpu.r[15]); self.stage[1] = cpu.fetch(u16, cpu.r[15] + 2); cpu.r[15] += 4; } else { self.stage[0] = cpu.fetch(u32, cpu.r[15]); self.stage[1] = cpu.fetch(u32, cpu.r[15] + 4); cpu.r[15] += 8; } self.flushed = true; } }; /// Bank of Registers from other CPU Modes pub const Bank = struct { /// Storage for r13_, r14_ /// e.g. [r13, r14, r13_svc, r14_svc] r: [2 * 6]u32, /// Storage for R8_fiq -> R12_fiq and their normal counterparts /// e.g [r[0 + 8], fiq_r[0 + 8], r[1 + 8], fiq_r[1 + 8]...] fiq: [2 * 5]u32, spsr: [5]PSR, const Kind = enum(u1) { R13 = 0, R14, }; pub fn create() Bank { return .{ .r = [_]u32{0x00} ** 12, .fiq = [_]u32{0x00} ** 10, .spsr = [_]PSR{.{ .raw = 0x0000_0000 }} ** 5, }; } // public so that we can set up fast-boot pub inline fn regIdx(mode: Mode, kind: Kind) usize { const idx: usize = switch (mode) { .User, .System => 0, .Supervisor => 1, .Abort => 2, .Undefined => 3, .Irq => 4, .Fiq => 5, }; return (idx * 2) + if (kind == .R14) @as(usize, 1) else 0; } inline fn spsrIdx(mode: Mode) usize { return switch (mode) { .Supervisor => 0, .Abort => 1, .Undefined => 2, .Irq => 3, .Fiq => 4, else => std.debug.panic("[CPU/Mode] {} does not have a SPSR Register", .{mode}), }; } inline fn fiqIdx(i: usize, mode: Mode) usize { return (i * 2) + if (mode == .Fiq) @as(usize, 1) else 0; } }; pub fn init(scheduler: Scheduler, bus: Bus) Self { return Self{ .sched = scheduler, .bus = bus, .cpsr = .{ .raw = 0x0000_001F }, .spsr = .{ .raw = 0x0000_0000 }, }; } // FIXME: Resetting disables logging (if enabled) pub fn reset(self: *Self) void { self.* = .{ .sched = self.sched, .bus = self.bus, .cpsr = .{ .raw = 0x0000_001F }, .spsr = .{ .raw = 0x0000_0000 }, }; } pub inline fn hasSPSR(self: *const Self) bool { const mode = Mode.getChecked(self, self.cpsr.mode.read()); return switch (mode) { .System, .User => false, else => true, }; } pub inline fn isPrivileged(self: *const Self) bool { const mode = Mode.getChecked(self, self.cpsr.mode.read()); return switch (mode) { .User => false, else => true, }; } pub fn setCpsr(self: *Self, value: u32) void { if (value & 0x1F != self.cpsr.raw & 0x1F) self.changeModeFromIdx(@truncate(u5, value & 0x1F)); self.cpsr.raw = value; } fn changeModeFromIdx(self: *Self, next: u5) void { self.changeMode(Mode.getChecked(self, next)); } pub fn setUserModeRegister(self: *Self, idx: usize, value: u32) void { const current = Mode.getChecked(self, self.cpsr.mode.read()); switch (idx) { 8...12 => { if (current == .Fiq) { self.bank.fiq[Bank.fiqIdx(idx - 8, .User)] = value; } else self.r[idx] = value; }, 13, 14 => switch (current) { .User, .System => self.r[idx] = value, else => { const kind = std.meta.intToEnum(Bank.Kind, idx - 13) catch unreachable; self.bank.r[Bank.regIdx(.User, kind)] = value; }, }, else => self.r[idx] = value, // R0 -> R7 and R15 } } pub fn getUserModeRegister(self: *Self, idx: usize) u32 { const current = Mode.getChecked(self, self.cpsr.mode.read()); return switch (idx) { 8...12 => if (current == .Fiq) self.bank.fiq[Bank.fiqIdx(idx - 8, .User)] else self.r[idx], 13, 14 => switch (current) { .User, .System => self.r[idx], else => blk: { const kind = std.meta.intToEnum(Bank.Kind, idx - 13) catch unreachable; break :blk self.bank.r[Bank.regIdx(.User, kind)]; }, }, else => self.r[idx], // R0 -> R7 and R15 }; } pub fn changeMode(self: *Self, next: Mode) void { const now = Mode.getChecked(self, self.cpsr.mode.read()); // Bank R8 -> r12 for (0..5) |i| { self.bank.fiq[Bank.fiqIdx(i, now)] = self.r[8 + i]; } // Bank r13, r14, SPSR switch (now) { .User, .System => { self.bank.r[Bank.regIdx(now, .R13)] = self.r[13]; self.bank.r[Bank.regIdx(now, .R14)] = self.r[14]; }, else => { self.bank.r[Bank.regIdx(now, .R13)] = self.r[13]; self.bank.r[Bank.regIdx(now, .R14)] = self.r[14]; self.bank.spsr[Bank.spsrIdx(now)] = self.spsr; }, } // Grab R8 -> R12 for (0..5) |i| { self.r[8 + i] = self.bank.fiq[Bank.fiqIdx(i, next)]; } // Grab r13, r14, SPSR switch (next) { .User, .System => { self.r[13] = self.bank.r[Bank.regIdx(next, .R13)]; self.r[14] = self.bank.r[Bank.regIdx(next, .R14)]; }, else => { self.r[13] = self.bank.r[Bank.regIdx(next, .R13)]; self.r[14] = self.bank.r[Bank.regIdx(next, .R14)]; self.spsr = self.bank.spsr[Bank.spsrIdx(next)]; }, } self.cpsr.mode.write(@enumToInt(next)); } pub fn step(self: *Self) void { defer { if (!self.pipe.flushed) self.r[15] += if (self.cpsr.t.read()) 2 else @as(u32, 4); self.pipe.flushed = false; } if (self.cpsr.t.read()) { const opcode = @truncate(u16, self.pipe.step(self, u16) orelse return); thumb.lut[thumb.idx(opcode)](self, self.bus, opcode); } else { const opcode = self.pipe.step(self, u32) orelse return; if (self.cpsr.check(@truncate(u4, opcode >> 28))) { arm.lut[arm.idx(opcode)](self, self.bus, opcode); } } } inline fn fetch(self: *Self, comptime T: type, address: u32) T { comptime std.debug.assert(T == u32 or T == u16); // Opcode may be 32-bit (ARM) or 16-bit (THUMB) // Bus.read will advance the scheduler. There are different timings for CPU fetches, // so we want to undo what Bus.read will apply. We can do this by caching the current tick // This is very dumb. // // FIXME: Please rework this // FIXME: Please Re-enable this // const tick_cache = self.sched.tick; // defer self.sched.tick = tick_cache + Bus.fetch_timings[@boolToInt(T == u32)][@truncate(u4, address >> 24)]; return self.bus.read(T, address); } pub fn panic(self: *const Self, comptime format: []const u8, args: anytype) noreturn { var i: usize = 0; while (i < 16) : (i += 4) { const i_1 = i + 1; const i_2 = i + 2; const i_3 = i + 3; std.debug.print("R{}: 0x{X:0>8}\tR{}: 0x{X:0>8}\tR{}: 0x{X:0>8}\tR{}: 0x{X:0>8}\n", .{ i, self.r[i], i_1, self.r[i_1], i_2, self.r[i_2], i_3, self.r[i_3] }); } std.debug.print("cpsr: 0x{X:0>8} ", .{self.cpsr.raw}); self.cpsr.toString(); std.debug.print("spsr: 0x{X:0>8} ", .{self.spsr.raw}); self.spsr.toString(); std.debug.print("pipeline: {??X:0>8}\n", .{self.pipe.stage}); if (self.cpsr.t.read()) { const opcode = self.bus.dbgRead(u16, self.r[15] - 4); const id = thumb.idx(opcode); std.debug.print("opcode: ID: 0x{b:0>10} 0x{X:0>4}\n", .{ id, opcode }); } else { const opcode = self.bus.dbgRead(u32, self.r[15] - 4); const id = arm.idx(opcode); std.debug.print("opcode: ID: 0x{X:0>3} 0x{X:0>8}\n", .{ id, opcode }); } std.debug.print("tick: {}\n\n", .{self.sched.now()}); std.debug.panic(format, args); } pub fn interface(self: *Self) Interpreter { return switch (arch) { .v4t => .{ .v4t = self }, .v5te => .{ .v5te = self }, }; } }; } pub const Mode = enum(u5) { User = 0b10000, Fiq = 0b10001, Irq = 0b10010, Supervisor = 0b10011, Abort = 0b10111, Undefined = 0b11011, System = 0b11111, pub fn toString(self: Mode) []const u8 { return switch (self) { .User => "usr", .Fiq => "fiq", .Irq => "irq", .Supervisor => "svc", .Abort => "abt", .Undefined => "und", .System => "sys", }; } fn get(bits: u5) ?Mode { return std.meta.intToEnum(Mode, bits) catch null; } fn getChecked(cpu: anytype, bits: u5) Mode { return get(bits) orelse cpu.panic("[CPU/CPSR] 0b{b:0>5} is an invalid CPU mode", .{bits}); } }; pub const PSR = extern union { mode: Bitfield(u32, 0, 5), t: Bit(u32, 5), f: Bit(u32, 6), i: Bit(u32, 7), v: Bit(u32, 28), c: Bit(u32, 29), z: Bit(u32, 30), n: Bit(u32, 31), raw: u32, fn toString(self: @This()) void { std.debug.print("[", .{}); if (self.n.read()) std.debug.print("N", .{}) else std.debug.print("-", .{}); if (self.z.read()) std.debug.print("Z", .{}) else std.debug.print("-", .{}); if (self.c.read()) std.debug.print("C", .{}) else std.debug.print("-", .{}); if (self.v.read()) std.debug.print("V", .{}) else std.debug.print("-", .{}); if (self.i.read()) std.debug.print("I", .{}) else std.debug.print("-", .{}); if (self.f.read()) std.debug.print("F", .{}) else std.debug.print("-", .{}); if (self.t.read()) std.debug.print("T", .{}) else std.debug.print("-", .{}); std.debug.print("|", .{}); if (Mode.get(self.mode.read())) |m| std.debug.print("{s}", .{m.toString()}) else std.debug.print("---", .{}); std.debug.print("]\n", .{}); } pub inline fn check(self: @This(), cond: u4) bool { const flags = @truncate(u4, self.raw >> 28); return condition_lut[cond] & (@as(u16, 1) << flags) != 0; } };