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:
@@ -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{};
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user