stage2: remove SPU Mark II backend

As it stands, the backend is incomplete, and there is no active contributor,
making it dead weight.

However, anyone is free to resurrect this backend at any time.
This commit is contained in:
Andrew Kelley
2021-05-15 21:25:42 -07:00
parent 7cd94d2123
commit 07606d12da
7 changed files with 1 additions and 543 deletions

View File

@@ -1,170 +0,0 @@
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,
pub fn allocIndex(self: Register) ?u4 {
return null;
}
};
pub const callee_preserved_regs = [_]Register{};

View File

@@ -1,166 +0,0 @@
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;
}
}
}
};
}