aboutsummaryrefslogtreecommitdiff
path: root/src-self-hosted/codegen
diff options
context:
space:
mode:
Diffstat (limited to 'src-self-hosted/codegen')
-rw-r--r--src-self-hosted/codegen/spu-mk2.zig161
-rw-r--r--src-self-hosted/codegen/spu-mk2/interpreter.zig166
2 files changed, 326 insertions, 1 deletions
diff --git a/src-self-hosted/codegen/spu-mk2.zig b/src-self-hosted/codegen/spu-mk2.zig
index 0a95dce663..542862caca 100644
--- a/src-self-hosted/codegen/spu-mk2.zig
+++ b/src-self-hosted/codegen/spu-mk2.zig
@@ -1,4 +1,163 @@
-pub usingnamespace @import("std").spu;
+const std = @import("std");
+
+pub const Interpreter = @import("spu-mk2/interpreter.zig").Interpreter;
+
+pub const ExecutionCondition = enum(u3) {
+ always = 0,
+ when_zero = 1,
+ not_zero = 2,
+ greater_zero = 3,
+ less_than_zero = 4,
+ greater_or_equal_zero = 5,
+ less_or_equal_zero = 6,
+ overflow = 7,
+};
+
+pub const InputBehaviour = enum(u2) {
+ zero = 0,
+ immediate = 1,
+ peek = 2,
+ pop = 3,
+};
+
+pub const OutputBehaviour = enum(u2) {
+ discard = 0,
+ push = 1,
+ jump = 2,
+ jump_relative = 3,
+};
+
+pub const Command = enum(u5) {
+ copy = 0,
+ ipget = 1,
+ get = 2,
+ set = 3,
+ store8 = 4,
+ store16 = 5,
+ load8 = 6,
+ load16 = 7,
+ undefined0 = 8,
+ undefined1 = 9,
+ frget = 10,
+ frset = 11,
+ bpget = 12,
+ bpset = 13,
+ spget = 14,
+ spset = 15,
+ add = 16,
+ sub = 17,
+ mul = 18,
+ div = 19,
+ mod = 20,
+ @"and" = 21,
+ @"or" = 22,
+ xor = 23,
+ not = 24,
+ signext = 25,
+ rol = 26,
+ ror = 27,
+ bswap = 28,
+ asr = 29,
+ lsl = 30,
+ lsr = 31,
+};
+
+pub const Instruction = packed struct {
+ condition: ExecutionCondition,
+ input0: InputBehaviour,
+ input1: InputBehaviour,
+ modify_flags: bool,
+ output: OutputBehaviour,
+ command: Command,
+ reserved: u1 = 0,
+
+ pub fn format(instr: Instruction, comptime fmt: []const u8, options: std.fmt.FormatOptions, out: anytype) !void {
+ try std.fmt.format(out, "0x{x:0<4} ", .{@bitCast(u16, instr)});
+ try out.writeAll(switch (instr.condition) {
+ .always => " ",
+ .when_zero => "== 0",
+ .not_zero => "!= 0",
+ .greater_zero => " > 0",
+ .less_than_zero => " < 0",
+ .greater_or_equal_zero => ">= 0",
+ .less_or_equal_zero => "<= 0",
+ .overflow => "ovfl",
+ });
+ try out.writeAll(" ");
+ try out.writeAll(switch (instr.input0) {
+ .zero => "zero",
+ .immediate => "imm ",
+ .peek => "peek",
+ .pop => "pop ",
+ });
+ try out.writeAll(" ");
+ try out.writeAll(switch (instr.input1) {
+ .zero => "zero",
+ .immediate => "imm ",
+ .peek => "peek",
+ .pop => "pop ",
+ });
+ try out.writeAll(" ");
+ try out.writeAll(switch (instr.command) {
+ .copy => "copy ",
+ .ipget => "ipget ",
+ .get => "get ",
+ .set => "set ",
+ .store8 => "store8 ",
+ .store16 => "store16 ",
+ .load8 => "load8 ",
+ .load16 => "load16 ",
+ .undefined0 => "undefined",
+ .undefined1 => "undefined",
+ .frget => "frget ",
+ .frset => "frset ",
+ .bpget => "bpget ",
+ .bpset => "bpset ",
+ .spget => "spget ",
+ .spset => "spset ",
+ .add => "add ",
+ .sub => "sub ",
+ .mul => "mul ",
+ .div => "div ",
+ .mod => "mod ",
+ .@"and" => "and ",
+ .@"or" => "or ",
+ .xor => "xor ",
+ .not => "not ",
+ .signext => "signext ",
+ .rol => "rol ",
+ .ror => "ror ",
+ .bswap => "bswap ",
+ .asr => "asr ",
+ .lsl => "lsl ",
+ .lsr => "lsr ",
+ });
+ try out.writeAll(" ");
+ try out.writeAll(switch (instr.output) {
+ .discard => "discard",
+ .push => "push ",
+ .jump => "jmp ",
+ .jump_relative => "rjmp ",
+ });
+ try out.writeAll(" ");
+ try out.writeAll(if (instr.modify_flags)
+ "+ flags"
+ else
+ " ");
+ }
+};
+
+pub const FlagRegister = packed struct {
+ zero: bool,
+ negative: bool,
+ carry: bool,
+ carry_enabled: bool,
+ interrupt0_enabled: bool,
+ interrupt1_enabled: bool,
+ interrupt2_enabled: bool,
+ interrupt3_enabled: bool,
+ reserved: u8 = 0,
+};
pub const Register = enum {
dummy,
diff --git a/src-self-hosted/codegen/spu-mk2/interpreter.zig b/src-self-hosted/codegen/spu-mk2/interpreter.zig
new file mode 100644
index 0000000000..1ec99546c6
--- /dev/null
+++ b/src-self-hosted/codegen/spu-mk2/interpreter.zig
@@ -0,0 +1,166 @@
+const std = @import("std");
+const log = std.log.scoped(.SPU_2_Interpreter);
+const spu = @import("../spu-mk2.zig");
+const FlagRegister = spu.FlagRegister;
+const Instruction = spu.Instruction;
+const ExecutionCondition = spu.ExecutionCondition;
+
+pub fn Interpreter(comptime Bus: type) type {
+ return struct {
+ ip: u16 = 0,
+ sp: u16 = undefined,
+ bp: u16 = undefined,
+ fr: FlagRegister = @bitCast(FlagRegister, @as(u16, 0)),
+ /// This is set to true when we hit an undefined0 instruction, allowing it to
+ /// be used as a trap for testing purposes
+ undefined0: bool = false,
+ /// This is set to true when we hit an undefined1 instruction, allowing it to
+ /// be used as a trap for testing purposes. undefined1 is used as a breakpoint.
+ undefined1: bool = false,
+ bus: Bus,
+
+ pub fn ExecuteBlock(self: *@This(), comptime size: ?u32) !void {
+ var count: usize = 0;
+ while (size == null or count < size.?) {
+ count += 1;
+ var instruction = @bitCast(Instruction, self.bus.read16(self.ip));
+
+ log.debug("Executing {}\n", .{instruction});
+
+ self.ip +%= 2;
+
+ const execute = switch (instruction.condition) {
+ .always => true,
+ .not_zero => !self.fr.zero,
+ .when_zero => self.fr.zero,
+ .overflow => self.fr.carry,
+ ExecutionCondition.greater_or_equal_zero => !self.fr.negative,
+ else => return error.Unimplemented,
+ };
+
+ if (execute) {
+ const val0 = switch (instruction.input0) {
+ .zero => @as(u16, 0),
+ .immediate => i: {
+ const val = self.bus.read16(@intCast(u16, self.ip));
+ self.ip +%= 2;
+ break :i val;
+ },
+ else => |e| e: {
+ // peek or pop; show value at current SP, and if pop, increment sp
+ const val = self.bus.read16(self.sp);
+ if (e == .pop) {
+ self.sp +%= 2;
+ }
+ break :e val;
+ },
+ };
+ const val1 = switch (instruction.input1) {
+ .zero => @as(u16, 0),
+ .immediate => i: {
+ const val = self.bus.read16(@intCast(u16, self.ip));
+ self.ip +%= 2;
+ break :i val;
+ },
+ else => |e| e: {
+ // peek or pop; show value at current SP, and if pop, increment sp
+ const val = self.bus.read16(self.sp);
+ if (e == .pop) {
+ self.sp +%= 2;
+ }
+ break :e val;
+ },
+ };
+
+ const output: u16 = switch (instruction.command) {
+ .get => self.bus.read16(self.bp +% (2 *% val0)),
+ .set => a: {
+ self.bus.write16(self.bp +% 2 *% val0, val1);
+ break :a val1;
+ },
+ .load8 => self.bus.read8(val0),
+ .load16 => self.bus.read16(val0),
+ .store8 => a: {
+ const val = @truncate(u8, val1);
+ self.bus.write8(val0, val);
+ break :a val;
+ },
+ .store16 => a: {
+ self.bus.write16(val0, val1);
+ break :a val1;
+ },
+ .copy => val0,
+ .add => a: {
+ var val: u16 = undefined;
+ self.fr.carry = @addWithOverflow(u16, val0, val1, &val);
+ break :a val;
+ },
+ .sub => a: {
+ var val: u16 = undefined;
+ self.fr.carry = @subWithOverflow(u16, val0, val1, &val);
+ break :a val;
+ },
+ .spset => a: {
+ self.sp = val0;
+ break :a val0;
+ },
+ .bpset => a: {
+ self.bp = val0;
+ break :a val0;
+ },
+ .frset => a: {
+ const val = (@bitCast(u16, self.fr) & val1) | (val0 & ~val1);
+ self.fr = @bitCast(FlagRegister, val);
+ break :a val;
+ },
+ .bswap => (val0 >> 8) | (val0 << 8),
+ .bpget => self.bp,
+ .spget => self.sp,
+ .ipget => self.ip +% (2 *% val0),
+ .lsl => val0 << 1,
+ .lsr => val0 >> 1,
+ .@"and" => val0 & val1,
+ .@"or" => val0 | val1,
+ .xor => val0 ^ val1,
+ .not => ~val0,
+ .undefined0 => {
+ self.undefined0 = true;
+ // Break out of the loop, and let the caller decide what to do
+ return;
+ },
+ .undefined1 => {
+ self.undefined1 = true;
+ // Break out of the loop, and let the caller decide what to do
+ return;
+ },
+ .signext => if ((val0 & 0x80) != 0)
+ (val0 & 0xFF) | 0xFF00
+ else
+ (val0 & 0xFF),
+ else => return error.Unimplemented,
+ };
+
+ switch (instruction.output) {
+ .discard => {},
+ .push => {
+ self.sp -%= 2;
+ self.bus.write16(self.sp, output);
+ },
+ .jump => {
+ self.ip = output;
+ },
+ else => return error.Unimplemented,
+ }
+ if (instruction.modify_flags) {
+ self.fr.negative = (output & 0x8000) != 0;
+ self.fr.zero = (output == 0x0000);
+ }
+ } else {
+ if (instruction.input0 == .immediate) self.ip +%= 2;
+ if (instruction.input1 == .immediate) self.ip +%= 2;
+ break;
+ }
+ }
+ }
+ };
+}