Compare commits
No commits in common. "ba22b856ecb3bd6fc43530dddf6ee79b4b458b30" and "6c81608c5914888858abea56514707712d64272a" have entirely different histories.
ba22b856ec
...
6c81608c59
24
build.zig
24
build.zig
|
@ -51,27 +51,3 @@ pub fn build(b: *std.Build) void {
|
||||||
const test_step = b.step("test", "Run library tests");
|
const test_step = b.step("test", "Run library tests");
|
||||||
test_step.dependOn(&run_lib_tests.step);
|
test_step.dependOn(&run_lib_tests.step);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `arm32` will expect the depender to supply the `zba-util` library via the package maanger
|
|
||||||
pub fn module(b: *std.Build) *std.Build.Module {
|
|
||||||
const bitfield = b.createModule(.{ .source_file = .{ .path = path("/lib/bitfield.zig") }, .dependencies = &.{} });
|
|
||||||
|
|
||||||
const zba_util = b.dependency("zba-util", .{}).module("zba-util");
|
|
||||||
|
|
||||||
return b.createModule(.{
|
|
||||||
.source_file = .{ .path = path("/src/lib.zig") },
|
|
||||||
.dependencies = &.{
|
|
||||||
.{ .name = "zba-util", .module = zba_util },
|
|
||||||
.{ .name = "bitfield", .module = bitfield },
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://github.com/MasterQ32/SDL.zig/blob/4d565b54227b862c1540719e0e21a36d649e87d5/build.zig#L114-L120
|
|
||||||
fn path(comptime suffix: []const u8) []const u8 {
|
|
||||||
if (suffix[0] != '/') @compileError("relToPath requires an absolute path!");
|
|
||||||
return comptime blk: {
|
|
||||||
const root_dir = std.fs.path.dirname(@src().file) orelse ".";
|
|
||||||
break :blk root_dir ++ suffix;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
30
src/arm.zig
30
src/arm.zig
|
@ -40,17 +40,9 @@ pub fn Arm32(comptime arch: Architecture) type {
|
||||||
|
|
||||||
bank: Bank = Bank.create(),
|
bank: Bank = Bank.create(),
|
||||||
|
|
||||||
const arm = switch (arch) {
|
const arm = @import("arm/v4t.zig").arm(Self);
|
||||||
.v4t => @import("arm/v4t.zig").arm,
|
const thumb = @import("arm/v4t.zig").thumb(Self);
|
||||||
.v5te => @import("arm/v5te.zig").arm,
|
|
||||||
};
|
|
||||||
|
|
||||||
const thumb = switch (arch) {
|
|
||||||
.v4t => @import("arm/v4t.zig").thumb,
|
|
||||||
.v5te => @import("arm/v5te.zig").thumb,
|
|
||||||
};
|
|
||||||
|
|
||||||
// FIXME: What about .v5te?
|
|
||||||
const Pipeline = struct {
|
const Pipeline = struct {
|
||||||
stage: [2]?u32,
|
stage: [2]?u32,
|
||||||
flushed: bool,
|
flushed: bool,
|
||||||
|
@ -148,7 +140,7 @@ pub fn Arm32(comptime arch: Architecture) type {
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn init(scheduler: Scheduler, bus: Bus) Self {
|
pub fn init(scheduler: Scheduler, bus: Bus) Self {
|
||||||
return .{
|
return Self{
|
||||||
.sched = scheduler,
|
.sched = scheduler,
|
||||||
.bus = bus,
|
.bus = bus,
|
||||||
.cpsr = .{ .raw = 0x0000_001F },
|
.cpsr = .{ .raw = 0x0000_001F },
|
||||||
|
@ -156,16 +148,6 @@ pub fn Arm32(comptime arch: Architecture) type {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// CPU needs it's own read/write fns due to ICTM and DCTM present in v5te
|
|
||||||
// I considered implementing Bus.cpu_read and Bus.cpu_write but ended up considering that a bit too leaky
|
|
||||||
pub fn read(self: *Self, comptime T: type, address: u32) T {
|
|
||||||
return self.bus.read(T, address);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write(self: *Self, comptime T: type, address: u32, value: T) void {
|
|
||||||
return self.bus.write(T, address, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: Resetting disables logging (if enabled)
|
// FIXME: Resetting disables logging (if enabled)
|
||||||
pub fn reset(self: *Self) void {
|
pub fn reset(self: *Self) void {
|
||||||
self.* = .{
|
self.* = .{
|
||||||
|
@ -287,12 +269,12 @@ pub fn Arm32(comptime arch: Architecture) type {
|
||||||
|
|
||||||
if (self.cpsr.t.read()) {
|
if (self.cpsr.t.read()) {
|
||||||
const opcode: u16 = @truncate(self.pipe.step(self, u16) orelse return);
|
const opcode: u16 = @truncate(self.pipe.step(self, u16) orelse return);
|
||||||
thumb.lut[thumb.idx(opcode)](self, opcode);
|
thumb.lut[thumb.idx(opcode)](self, self.bus, opcode);
|
||||||
} else {
|
} else {
|
||||||
const opcode = self.pipe.step(self, u32) orelse return;
|
const opcode = self.pipe.step(self, u32) orelse return;
|
||||||
|
|
||||||
if (self.cpsr.check(@truncate(opcode >> 28))) {
|
if (self.cpsr.check(@truncate(opcode >> 28))) {
|
||||||
arm.lut[arm.idx(opcode)](self, opcode);
|
arm.lut[arm.idx(opcode)](self, self.bus, opcode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -309,7 +291,7 @@ pub fn Arm32(comptime arch: Architecture) type {
|
||||||
// const tick_cache = self.sched.tick;
|
// const tick_cache = self.sched.tick;
|
||||||
// defer self.sched.tick = tick_cache + Bus.fetch_timings[@boolToInt(T == u32)][@truncate(u4, address >> 24)];
|
// defer self.sched.tick = tick_cache + Bus.fetch_timings[@boolToInt(T == u32)][@truncate(u4, address >> 24)];
|
||||||
|
|
||||||
return self.read(T, address);
|
return self.bus.read(T, address);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn panic(self: *const Self, comptime format: []const u8, args: anytype) noreturn {
|
pub fn panic(self: *const Self, comptime format: []const u8, args: anytype) noreturn {
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
|
const Bus = @import("../../../lib.zig").Bus;
|
||||||
|
|
||||||
pub fn blockDataTransfer(comptime InstrFn: type, comptime P: bool, comptime U: bool, comptime S: bool, comptime W: bool, comptime L: bool) InstrFn {
|
pub fn blockDataTransfer(comptime InstrFn: type, comptime P: bool, comptime U: bool, comptime S: bool, comptime W: bool, comptime L: bool) InstrFn {
|
||||||
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
fn inner(cpu: Arm32, opcode: u32) void {
|
fn inner(cpu: Arm32, bus: Bus, opcode: u32) void {
|
||||||
const rn: u4 = @truncate(opcode >> 16 & 0xF);
|
const rn: u4 = @truncate(opcode >> 16 & 0xF);
|
||||||
const rlist = opcode & 0xFFFF;
|
const rlist = opcode & 0xFFFF;
|
||||||
const r15 = rlist >> 15 & 1 == 1;
|
const r15 = rlist >> 15 & 1 == 1;
|
||||||
|
@ -52,10 +54,10 @@ pub fn blockDataTransfer(comptime InstrFn: type, comptime P: bool, comptime U: b
|
||||||
}
|
}
|
||||||
|
|
||||||
if (L) {
|
if (L) {
|
||||||
cpu.r[15] = cpu.read(u32, und_addr);
|
cpu.r[15] = bus.read(u32, und_addr);
|
||||||
cpu.pipe.reload(cpu);
|
cpu.pipe.reload(cpu);
|
||||||
} else {
|
} else {
|
||||||
cpu.write(u32, und_addr, cpu.r[15] + 4);
|
bus.write(u32, und_addr, cpu.r[15] + 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
cpu.r[rn] = if (U) cpu.r[rn] + 0x40 else cpu.r[rn] - 0x40;
|
cpu.r[rn] = if (U) cpu.r[rn] + 0x40 else cpu.r[rn] - 0x40;
|
||||||
|
@ -65,7 +67,7 @@ pub fn blockDataTransfer(comptime InstrFn: type, comptime P: bool, comptime U: b
|
||||||
i = first;
|
i = first;
|
||||||
while (i < 16) : (i += 1) {
|
while (i < 16) : (i += 1) {
|
||||||
if (rlist >> i & 1 == 1) {
|
if (rlist >> i & 1 == 1) {
|
||||||
transfer(cpu, r15, i, address);
|
transfer(cpu, bus, r15, i, address);
|
||||||
address += 4;
|
address += 4;
|
||||||
|
|
||||||
if (W and !L and write_to_base) {
|
if (W and !L and write_to_base) {
|
||||||
|
@ -78,13 +80,13 @@ pub fn blockDataTransfer(comptime InstrFn: type, comptime P: bool, comptime U: b
|
||||||
if (W and L and rlist >> rn & 1 == 0) cpu.r[rn] = new_base;
|
if (W and L and rlist >> rn & 1 == 0) cpu.r[rn] = new_base;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transfer(cpu: Arm32, r15_present: bool, i: u5, address: u32) void {
|
fn transfer(cpu: Arm32, bus: Bus, r15_present: bool, i: u5, address: u32) void {
|
||||||
if (L) {
|
if (L) {
|
||||||
if (S and !r15_present) {
|
if (S and !r15_present) {
|
||||||
// Always Transfer User mode Registers
|
// Always Transfer User mode Registers
|
||||||
cpu.setUserModeRegister(i, cpu.read(u32, address));
|
cpu.setUserModeRegister(i, bus.read(u32, address));
|
||||||
} else {
|
} else {
|
||||||
const value = cpu.read(u32, address);
|
const value = bus.read(u32, address);
|
||||||
|
|
||||||
cpu.r[i] = value;
|
cpu.r[i] = value;
|
||||||
if (i == 0xF) {
|
if (i == 0xF) {
|
||||||
|
@ -99,9 +101,9 @@ pub fn blockDataTransfer(comptime InstrFn: type, comptime P: bool, comptime U: b
|
||||||
// Always Transfer User mode Registers
|
// Always Transfer User mode Registers
|
||||||
// This happens regardless if r15 is in the list
|
// This happens regardless if r15 is in the list
|
||||||
const value = cpu.getUserModeRegister(i);
|
const value = cpu.getUserModeRegister(i);
|
||||||
cpu.write(u32, address, value + if (i == 0xF) 4 else @as(u32, 0)); // PC is already 8 ahead to make 12
|
bus.write(u32, address, value + if (i == 0xF) 4 else @as(u32, 0)); // PC is already 8 ahead to make 12
|
||||||
} else {
|
} else {
|
||||||
cpu.write(u32, address, cpu.r[i] + if (i == 0xF) 4 else @as(u32, 0));
|
bus.write(u32, address, cpu.r[i] + if (i == 0xF) 4 else @as(u32, 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
|
const Bus = @import("../../../lib.zig").Bus;
|
||||||
const sext = @import("zba-util").sext;
|
const sext = @import("zba-util").sext;
|
||||||
|
|
||||||
pub fn branch(comptime InstrFn: type, comptime L: bool) InstrFn {
|
pub fn branch(comptime InstrFn: type, comptime L: bool) InstrFn {
|
||||||
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
fn inner(cpu: Arm32, opcode: u32) void {
|
fn inner(cpu: Arm32, _: Bus, opcode: u32) void {
|
||||||
if (L) cpu.r[14] = cpu.r[15] - 4;
|
if (L) cpu.r[14] = cpu.r[15] - 4;
|
||||||
|
|
||||||
cpu.r[15] +%= sext(u32, u24, opcode) << 2;
|
cpu.r[15] +%= sext(u32, u24, opcode) << 2;
|
||||||
|
@ -17,7 +18,7 @@ pub fn branchAndExchange(comptime InstrFn: type) InstrFn {
|
||||||
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
pub fn inner(cpu: Arm32, opcode: u32) void {
|
pub fn inner(cpu: Arm32, _: Bus, opcode: u32) void {
|
||||||
const rn = opcode & 0xF;
|
const rn = opcode & 0xF;
|
||||||
|
|
||||||
const thumb = cpu.r[rn] & 1 == 1;
|
const thumb = cpu.r[rn] & 1 == 1;
|
||||||
|
|
|
@ -1,49 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const log = std.log.scoped(.coprocessor_handler);
|
|
||||||
|
|
||||||
pub fn dataTransfer(comptime InstrFn: type, comptime P: bool, comptime U: bool, comptime N: bool, comptime W: bool, comptime L: bool) InstrFn {
|
|
||||||
_ = L;
|
|
||||||
_ = W;
|
|
||||||
_ = N;
|
|
||||||
_ = U;
|
|
||||||
_ = P;
|
|
||||||
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
|
||||||
|
|
||||||
return struct {
|
|
||||||
fn inner(cpu: Arm32, opcode: u32) void {
|
|
||||||
_ = cpu;
|
|
||||||
|
|
||||||
log.err("TODO: handle 0x{X:0>8} which is a coprocessor data transfer instr", .{opcode});
|
|
||||||
}
|
|
||||||
}.inner;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn registerTransfer(comptime InstrFn: type, comptime opcode1: u3, comptime L: bool, comptime opcode2: u3) InstrFn {
|
|
||||||
_ = opcode2;
|
|
||||||
_ = L;
|
|
||||||
_ = opcode1;
|
|
||||||
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
|
||||||
|
|
||||||
return struct {
|
|
||||||
fn inner(cpu: Arm32, opcode: u32) void {
|
|
||||||
_ = cpu;
|
|
||||||
|
|
||||||
log.err("TODO: handle 0x{X:0>8} which is a coprocessor register transfer instr", .{opcode});
|
|
||||||
}
|
|
||||||
}.inner;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn dataProcessing(comptime InstrFn: type, comptime opcode1: u4, comptime opcode2: u3) InstrFn {
|
|
||||||
_ = opcode2;
|
|
||||||
_ = opcode1;
|
|
||||||
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
|
||||||
|
|
||||||
return struct {
|
|
||||||
fn inner(cpu: Arm32, opcode: u32) void {
|
|
||||||
_ = cpu;
|
|
||||||
|
|
||||||
log.err("TODO: handle 0x{X:0>8} which is a coprocessor data processing instr", .{opcode});
|
|
||||||
}
|
|
||||||
}.inner;
|
|
||||||
}
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
const Bus = @import("../../../lib.zig").Bus;
|
||||||
|
|
||||||
const exec = @import("../barrel_shifter.zig").exec;
|
const exec = @import("../barrel_shifter.zig").exec;
|
||||||
const ror = @import("../barrel_shifter.zig").ror;
|
const ror = @import("../barrel_shifter.zig").ror;
|
||||||
|
|
||||||
|
@ -5,7 +7,7 @@ pub fn dataProcessing(comptime InstrFn: type, comptime I: bool, comptime S: bool
|
||||||
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
fn inner(cpu: Arm32, opcode: u32) void {
|
fn inner(cpu: Arm32, _: Bus, opcode: u32) void {
|
||||||
const rd: u4 = @truncate(opcode >> 12 & 0xF);
|
const rd: u4 = @truncate(opcode >> 12 & 0xF);
|
||||||
const rn = opcode >> 16 & 0xF;
|
const rn = opcode >> 16 & 0xF;
|
||||||
const old_carry = @intFromBool(cpu.cpsr.c.read());
|
const old_carry = @intFromBool(cpu.cpsr.c.read());
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
const Bus = @import("../../../lib.zig").Bus;
|
||||||
|
|
||||||
const sext = @import("zba-util").sext;
|
const sext = @import("zba-util").sext;
|
||||||
const rotr = @import("zba-util").rotr;
|
const rotr = @import("zba-util").rotr;
|
||||||
|
|
||||||
|
@ -5,7 +7,7 @@ pub fn halfAndSignedDataTransfer(comptime InstrFn: type, comptime P: bool, compt
|
||||||
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
fn inner(cpu: Arm32, opcode: u32) void {
|
fn inner(cpu: Arm32, bus: Bus, opcode: u32) void {
|
||||||
const rn = opcode >> 16 & 0xF;
|
const rn = opcode >> 16 & 0xF;
|
||||||
const rd = opcode >> 12 & 0xF;
|
const rd = opcode >> 12 & 0xF;
|
||||||
const rm = opcode & 0xF;
|
const rm = opcode & 0xF;
|
||||||
|
@ -22,16 +24,16 @@ pub fn halfAndSignedDataTransfer(comptime InstrFn: type, comptime P: bool, compt
|
||||||
switch (@as(u2, @truncate(opcode >> 5))) {
|
switch (@as(u2, @truncate(opcode >> 5))) {
|
||||||
0b01 => {
|
0b01 => {
|
||||||
// LDRH
|
// LDRH
|
||||||
const value = cpu.read(u16, address);
|
const value = bus.read(u16, address);
|
||||||
result = rotr(u32, value, 8 * (address & 1));
|
result = rotr(u32, value, 8 * (address & 1));
|
||||||
},
|
},
|
||||||
0b10 => {
|
0b10 => {
|
||||||
// LDRSB
|
// LDRSB
|
||||||
result = sext(u32, u8, cpu.read(u8, address));
|
result = sext(u32, u8, bus.read(u8, address));
|
||||||
},
|
},
|
||||||
0b11 => {
|
0b11 => {
|
||||||
// LDRSH
|
// LDRSH
|
||||||
const value = cpu.read(u16, address);
|
const value = bus.read(u16, address);
|
||||||
|
|
||||||
// FIXME: I shouldn't have to use @as(u8, ...) here
|
// FIXME: I shouldn't have to use @as(u8, ...) here
|
||||||
result = if (address & 1 == 1) sext(u32, u8, @as(u8, @truncate(value >> 8))) else sext(u32, u16, value);
|
result = if (address & 1 == 1) sext(u32, u8, @as(u8, @truncate(value >> 8))) else sext(u32, u16, value);
|
||||||
|
@ -43,7 +45,7 @@ pub fn halfAndSignedDataTransfer(comptime InstrFn: type, comptime P: bool, compt
|
||||||
// STRH
|
// STRH
|
||||||
|
|
||||||
// FIXME: I shouldn't have to use @as(u8, ...) here
|
// FIXME: I shouldn't have to use @as(u8, ...) here
|
||||||
cpu.write(u16, address, @as(u16, @truncate(cpu.r[rd])));
|
bus.write(u16, address, @as(u16, @truncate(cpu.r[rd])));
|
||||||
} else unreachable; // SWP
|
} else unreachable; // SWP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
|
const Bus = @import("../../../lib.zig").Bus;
|
||||||
|
|
||||||
pub fn multiply(comptime InstrFn: type, comptime A: bool, comptime S: bool) InstrFn {
|
pub fn multiply(comptime InstrFn: type, comptime A: bool, comptime S: bool) InstrFn {
|
||||||
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
fn inner(cpu: Arm32, opcode: u32) void {
|
fn inner(cpu: Arm32, _: Bus, opcode: u32) void {
|
||||||
const rd = opcode >> 16 & 0xF;
|
const rd = opcode >> 16 & 0xF;
|
||||||
const rn = opcode >> 12 & 0xF;
|
const rn = opcode >> 12 & 0xF;
|
||||||
const rs = opcode >> 8 & 0xF;
|
const rs = opcode >> 8 & 0xF;
|
||||||
|
@ -25,7 +27,7 @@ pub fn multiplyLong(comptime InstrFn: type, comptime U: bool, comptime A: bool,
|
||||||
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
fn inner(cpu: Arm32, opcode: u32) void {
|
fn inner(cpu: Arm32, _: Bus, opcode: u32) void {
|
||||||
const rd_hi = opcode >> 16 & 0xF;
|
const rd_hi = opcode >> 16 & 0xF;
|
||||||
const rd_lo = opcode >> 12 & 0xF;
|
const rd_lo = opcode >> 12 & 0xF;
|
||||||
const rs = opcode >> 8 & 0xF;
|
const rs = opcode >> 8 & 0xF;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
|
const Bus = @import("../../../lib.zig").Bus;
|
||||||
const PSR = @import("../../../arm.zig").PSR;
|
const PSR = @import("../../../arm.zig").PSR;
|
||||||
|
|
||||||
const log = std.log.scoped(.PsrTransfer);
|
const log = std.log.scoped(.PsrTransfer);
|
||||||
|
@ -10,7 +11,7 @@ pub fn psrTransfer(comptime InstrFn: type, comptime I: bool, comptime R: bool, c
|
||||||
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
fn inner(cpu: Arm32, opcode: u32) void {
|
fn inner(cpu: Arm32, _: Bus, opcode: u32) void {
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
0b00 => {
|
0b00 => {
|
||||||
// MRS
|
// MRS
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
|
const Bus = @import("../../../lib.zig").Bus;
|
||||||
|
|
||||||
const rotr = @import("zba-util").rotr;
|
const rotr = @import("zba-util").rotr;
|
||||||
|
|
||||||
pub fn singleDataSwap(comptime InstrFn: type, comptime B: bool) InstrFn {
|
pub fn singleDataSwap(comptime InstrFn: type, comptime B: bool) InstrFn {
|
||||||
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
fn inner(cpu: Arm32, opcode: u32) void {
|
fn inner(cpu: Arm32, bus: Bus, opcode: u32) void {
|
||||||
const rn = opcode >> 16 & 0xF;
|
const rn = opcode >> 16 & 0xF;
|
||||||
const rd = opcode >> 12 & 0xF;
|
const rd = opcode >> 12 & 0xF;
|
||||||
const rm = opcode & 0xF;
|
const rm = opcode & 0xF;
|
||||||
|
@ -13,15 +15,15 @@ pub fn singleDataSwap(comptime InstrFn: type, comptime B: bool) InstrFn {
|
||||||
|
|
||||||
if (B) {
|
if (B) {
|
||||||
// SWPB
|
// SWPB
|
||||||
const value = cpu.read(u8, address);
|
const value = bus.read(u8, address);
|
||||||
|
|
||||||
// FIXME: I shouldn't have to use @as(u8, ...) here
|
// FIXME: I shouldn't have to use @as(u8, ...) here
|
||||||
cpu.write(u8, address, @as(u8, @truncate(cpu.r[rm])));
|
bus.write(u8, address, @as(u8, @truncate(cpu.r[rm])));
|
||||||
cpu.r[rd] = value;
|
cpu.r[rd] = value;
|
||||||
} else {
|
} else {
|
||||||
// SWP
|
// SWP
|
||||||
const value = rotr(u32, cpu.read(u32, address), 8 * (address & 0x3));
|
const value = rotr(u32, bus.read(u32, address), 8 * (address & 0x3));
|
||||||
cpu.write(u32, address, cpu.r[rm]);
|
bus.write(u32, address, cpu.r[rm]);
|
||||||
cpu.r[rd] = value;
|
cpu.r[rd] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
const shifter = @import("../barrel_shifter.zig");
|
const shifter = @import("../barrel_shifter.zig");
|
||||||
|
const Bus = @import("../../../lib.zig").Bus;
|
||||||
|
|
||||||
const rotr = @import("zba-util").rotr;
|
const rotr = @import("zba-util").rotr;
|
||||||
|
|
||||||
|
@ -6,7 +7,7 @@ pub fn singleDataTransfer(comptime InstrFn: type, comptime I: bool, comptime P:
|
||||||
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
fn inner(cpu: Arm32, opcode: u32) void {
|
fn inner(cpu: Arm32, bus: Bus, opcode: u32) void {
|
||||||
const rn = opcode >> 16 & 0xF;
|
const rn = opcode >> 16 & 0xF;
|
||||||
const rd = opcode >> 12 & 0xF;
|
const rd = opcode >> 12 & 0xF;
|
||||||
|
|
||||||
|
@ -20,10 +21,10 @@ pub fn singleDataTransfer(comptime InstrFn: type, comptime I: bool, comptime P:
|
||||||
if (L) {
|
if (L) {
|
||||||
if (B) {
|
if (B) {
|
||||||
// LDRB
|
// LDRB
|
||||||
result = cpu.read(u8, address);
|
result = bus.read(u8, address);
|
||||||
} else {
|
} else {
|
||||||
// LDR
|
// LDR
|
||||||
const value = cpu.read(u32, address);
|
const value = bus.read(u32, address);
|
||||||
result = rotr(u32, value, 8 * (address & 0x3));
|
result = rotr(u32, value, 8 * (address & 0x3));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -32,11 +33,11 @@ pub fn singleDataTransfer(comptime InstrFn: type, comptime I: bool, comptime P:
|
||||||
const value = cpu.r[rd] + if (rd == 0xF) 4 else @as(u32, 0); // PC is 12 ahead
|
const value = cpu.r[rd] + if (rd == 0xF) 4 else @as(u32, 0); // PC is 12 ahead
|
||||||
|
|
||||||
// FIXME: I shouldn't have to use @as(u8, ...) here
|
// FIXME: I shouldn't have to use @as(u8, ...) here
|
||||||
cpu.write(u8, address, @as(u8, @truncate(value)));
|
bus.write(u8, address, @as(u8, @truncate(value)));
|
||||||
} else {
|
} else {
|
||||||
// STR
|
// STR
|
||||||
const value = cpu.r[rd] + if (rd == 0xF) 4 else @as(u32, 0);
|
const value = cpu.r[rd] + if (rd == 0xF) 4 else @as(u32, 0);
|
||||||
cpu.write(u32, address, value);
|
bus.write(u32, address, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
|
const Bus = @import("../../../lib.zig").Bus;
|
||||||
|
|
||||||
pub fn armSoftwareInterrupt(comptime InstrFn: type) InstrFn {
|
pub fn armSoftwareInterrupt(comptime InstrFn: type) InstrFn {
|
||||||
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
fn inner(cpu: Arm32, _: u32) void {
|
fn inner(cpu: Arm32, _: Bus, _: u32) void {
|
||||||
// Copy Values from Current Mode
|
// Copy Values from Current Mode
|
||||||
const ret_addr = cpu.r[15] - 4;
|
const ret_addr = cpu.r[15] - 4;
|
||||||
const cpsr = cpu.cpsr.raw;
|
const cpsr = cpu.cpsr.raw;
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
const Bus = @import("../../../lib.zig").Bus;
|
||||||
|
|
||||||
const adc = @import("../arm/data_processing.zig").adc;
|
const adc = @import("../arm/data_processing.zig").adc;
|
||||||
const sbc = @import("../arm/data_processing.zig").sbc;
|
const sbc = @import("../arm/data_processing.zig").sbc;
|
||||||
|
|
||||||
|
@ -10,7 +12,7 @@ pub fn fmt4(comptime InstrFn: type, comptime op: u4) InstrFn {
|
||||||
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
fn inner(cpu: Arm32, opcode: u16) void {
|
fn inner(cpu: Arm32, _: Bus, opcode: u16) void {
|
||||||
const rs = opcode >> 3 & 0x7;
|
const rs = opcode >> 3 & 0x7;
|
||||||
const rd = opcode & 0x7;
|
const rd = opcode & 0x7;
|
||||||
const carry = @intFromBool(cpu.cpsr.c.read());
|
const carry = @intFromBool(cpu.cpsr.c.read());
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
|
const Bus = @import("../../../lib.zig").Bus;
|
||||||
|
|
||||||
pub fn fmt14(comptime InstrFn: type, comptime L: bool, comptime R: bool) InstrFn {
|
pub fn fmt14(comptime InstrFn: type, comptime L: bool, comptime R: bool) InstrFn {
|
||||||
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
fn inner(cpu: Arm32, opcode: u16) void {
|
fn inner(cpu: Arm32, bus: Bus, opcode: u16) void {
|
||||||
const count = @intFromBool(R) + countRlist(opcode);
|
const count = @intFromBool(R) + countRlist(opcode);
|
||||||
const start = cpu.r[13] - if (!L) count * 4 else 0;
|
const start = cpu.r[13] - if (!L) count * 4 else 0;
|
||||||
|
|
||||||
|
@ -19,9 +21,9 @@ pub fn fmt14(comptime InstrFn: type, comptime L: bool, comptime R: bool) InstrFn
|
||||||
while (i < 8) : (i += 1) {
|
while (i < 8) : (i += 1) {
|
||||||
if (opcode >> i & 1 == 1) {
|
if (opcode >> i & 1 == 1) {
|
||||||
if (L) {
|
if (L) {
|
||||||
cpu.r[i] = cpu.read(u32, address);
|
cpu.r[i] = bus.read(u32, address);
|
||||||
} else {
|
} else {
|
||||||
cpu.write(u32, address, cpu.r[i]);
|
bus.write(u32, address, cpu.r[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
address += 4;
|
address += 4;
|
||||||
|
@ -30,11 +32,11 @@ pub fn fmt14(comptime InstrFn: type, comptime L: bool, comptime R: bool) InstrFn
|
||||||
|
|
||||||
if (R) {
|
if (R) {
|
||||||
if (L) {
|
if (L) {
|
||||||
const value = cpu.read(u32, address);
|
const value = bus.read(u32, address);
|
||||||
cpu.r[15] = value & ~@as(u32, 1);
|
cpu.r[15] = value & ~@as(u32, 1);
|
||||||
cpu.pipe.reload(cpu);
|
cpu.pipe.reload(cpu);
|
||||||
} else {
|
} else {
|
||||||
cpu.write(u32, address, cpu.r[14]);
|
bus.write(u32, address, cpu.r[14]);
|
||||||
}
|
}
|
||||||
address += 4;
|
address += 4;
|
||||||
}
|
}
|
||||||
|
@ -48,16 +50,16 @@ pub fn fmt15(comptime InstrFn: type, comptime L: bool, comptime rb: u3) InstrFn
|
||||||
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
fn inner(cpu: Arm32, opcode: u16) void {
|
fn inner(cpu: Arm32, bus: Bus, opcode: u16) void {
|
||||||
var address = cpu.r[rb];
|
var address = cpu.r[rb];
|
||||||
const end_address = cpu.r[rb] + 4 * countRlist(opcode);
|
const end_address = cpu.r[rb] + 4 * countRlist(opcode);
|
||||||
|
|
||||||
if (opcode & 0xFF == 0) {
|
if (opcode & 0xFF == 0) {
|
||||||
if (L) {
|
if (L) {
|
||||||
cpu.r[15] = cpu.read(u32, address);
|
cpu.r[15] = bus.read(u32, address);
|
||||||
cpu.pipe.reload(cpu);
|
cpu.pipe.reload(cpu);
|
||||||
} else {
|
} else {
|
||||||
cpu.write(u32, address, cpu.r[15] + 2);
|
bus.write(u32, address, cpu.r[15] + 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
cpu.r[rb] += 0x40;
|
cpu.r[rb] += 0x40;
|
||||||
|
@ -70,9 +72,9 @@ pub fn fmt15(comptime InstrFn: type, comptime L: bool, comptime rb: u3) InstrFn
|
||||||
while (i < 8) : (i += 1) {
|
while (i < 8) : (i += 1) {
|
||||||
if (opcode >> i & 1 == 1) {
|
if (opcode >> i & 1 == 1) {
|
||||||
if (L) {
|
if (L) {
|
||||||
cpu.r[i] = cpu.read(u32, address);
|
cpu.r[i] = bus.read(u32, address);
|
||||||
} else {
|
} else {
|
||||||
cpu.write(u32, address, cpu.r[i]);
|
bus.write(u32, address, cpu.r[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!L and first_write) {
|
if (!L and first_write) {
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
|
const Bus = @import("../../../lib.zig").Bus;
|
||||||
|
|
||||||
const sext = @import("zba-util").sext;
|
const sext = @import("zba-util").sext;
|
||||||
|
|
||||||
pub fn fmt16(comptime InstrFn: type, comptime cond: u4) InstrFn {
|
pub fn fmt16(comptime InstrFn: type, comptime cond: u4) InstrFn {
|
||||||
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
fn inner(cpu: Arm32, opcode: u16) void {
|
fn inner(cpu: Arm32, _: Bus, opcode: u16) void {
|
||||||
// B
|
// B
|
||||||
if (cond == 0xE or cond == 0xF)
|
if (cond == 0xE or cond == 0xF)
|
||||||
cpu.panic("[CPU/THUMB.16] Undefined conditional branch with condition {}", .{cond});
|
cpu.panic("[CPU/THUMB.16] Undefined conditional branch with condition {}", .{cond});
|
||||||
|
@ -22,7 +24,7 @@ pub fn fmt18(comptime InstrFn: type) InstrFn {
|
||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
// B but conditional
|
// B but conditional
|
||||||
fn inner(cpu: Arm32, opcode: u16) void {
|
fn inner(cpu: Arm32, _: Bus, opcode: u16) void {
|
||||||
cpu.r[15] +%= sext(u32, u11, opcode & 0x7FF) << 1;
|
cpu.r[15] +%= sext(u32, u11, opcode & 0x7FF) << 1;
|
||||||
cpu.pipe.reload(cpu);
|
cpu.pipe.reload(cpu);
|
||||||
}
|
}
|
||||||
|
@ -33,7 +35,7 @@ pub fn fmt19(comptime InstrFn: type, comptime is_low: bool) InstrFn {
|
||||||
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
fn inner(cpu: Arm32, opcode: u16) void {
|
fn inner(cpu: Arm32, _: Bus, opcode: u16) void {
|
||||||
// BL
|
// BL
|
||||||
const offset = opcode & 0x7FF;
|
const offset = opcode & 0x7FF;
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
const Bus = @import("../../../lib.zig").Bus;
|
||||||
|
|
||||||
const add = @import("../arm/data_processing.zig").add;
|
const add = @import("../arm/data_processing.zig").add;
|
||||||
|
|
||||||
const lsl = @import("../barrel_shifter.zig").lsl;
|
const lsl = @import("../barrel_shifter.zig").lsl;
|
||||||
|
@ -8,7 +10,7 @@ pub fn fmt1(comptime InstrFn: type, comptime op: u2, comptime offset: u5) InstrF
|
||||||
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
fn inner(cpu: Arm32, opcode: u16) void {
|
fn inner(cpu: Arm32, _: Bus, opcode: u16) void {
|
||||||
const rs = opcode >> 3 & 0x7;
|
const rs = opcode >> 3 & 0x7;
|
||||||
const rd = opcode & 0x7;
|
const rd = opcode & 0x7;
|
||||||
|
|
||||||
|
@ -56,7 +58,7 @@ pub fn fmt5(comptime InstrFn: type, comptime op: u2, comptime h1: u1, comptime h
|
||||||
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
fn inner(cpu: Arm32, opcode: u16) void {
|
fn inner(cpu: Arm32, _: Bus, opcode: u16) void {
|
||||||
const rs = @as(u4, h2) << 3 | (opcode >> 3 & 0x7);
|
const rs = @as(u4, h2) << 3 | (opcode >> 3 & 0x7);
|
||||||
const rd = @as(u4, h1) << 3 | (opcode & 0x7);
|
const rd = @as(u4, h1) << 3 | (opcode & 0x7);
|
||||||
|
|
||||||
|
@ -111,7 +113,7 @@ pub fn fmt2(comptime InstrFn: type, comptime I: bool, is_sub: bool, rn: u3) Inst
|
||||||
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
fn inner(cpu: Arm32, opcode: u16) void {
|
fn inner(cpu: Arm32, _: Bus, opcode: u16) void {
|
||||||
const rs = opcode >> 3 & 0x7;
|
const rs = opcode >> 3 & 0x7;
|
||||||
const rd: u3 = @truncate(opcode);
|
const rd: u3 = @truncate(opcode);
|
||||||
const op1 = cpu.r[rs];
|
const op1 = cpu.r[rs];
|
||||||
|
@ -145,7 +147,7 @@ pub fn fmt3(comptime InstrFn: type, comptime op: u2, comptime rd: u3) InstrFn {
|
||||||
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
fn inner(cpu: Arm32, opcode: u16) void {
|
fn inner(cpu: Arm32, _: Bus, opcode: u16) void {
|
||||||
const op1 = cpu.r[rd];
|
const op1 = cpu.r[rd];
|
||||||
const op2: u32 = opcode & 0xFF; // Offset
|
const op2: u32 = opcode & 0xFF; // Offset
|
||||||
|
|
||||||
|
@ -185,7 +187,7 @@ pub fn fmt12(comptime InstrFn: type, comptime isSP: bool, comptime rd: u3) Instr
|
||||||
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
fn inner(cpu: Arm32, opcode: u16) void {
|
fn inner(cpu: Arm32, _: Bus, opcode: u16) void {
|
||||||
// ADD
|
// ADD
|
||||||
const left = if (isSP) cpu.r[13] else cpu.r[15] & ~@as(u32, 2);
|
const left = if (isSP) cpu.r[13] else cpu.r[15] & ~@as(u32, 2);
|
||||||
const right = (opcode & 0xFF) << 2;
|
const right = (opcode & 0xFF) << 2;
|
||||||
|
@ -198,7 +200,7 @@ pub fn fmt13(comptime InstrFn: type, comptime S: bool) InstrFn {
|
||||||
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
fn inner(cpu: Arm32, opcode: u16) void {
|
fn inner(cpu: Arm32, _: Bus, opcode: u16) void {
|
||||||
// ADD
|
// ADD
|
||||||
const offset = (opcode & 0x7F) << 2;
|
const offset = (opcode & 0x7F) << 2;
|
||||||
cpu.r[13] = if (S) cpu.r[13] - offset else cpu.r[13] + offset;
|
cpu.r[13] = if (S) cpu.r[13] - offset else cpu.r[13] + offset;
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
const Bus = @import("../../../lib.zig").Bus;
|
||||||
|
|
||||||
const rotr = @import("zba-util").rotr;
|
const rotr = @import("zba-util").rotr;
|
||||||
const sext = @import("zba-util").sext;
|
const sext = @import("zba-util").sext;
|
||||||
|
|
||||||
|
@ -5,12 +7,12 @@ pub fn fmt6(comptime InstrFn: type, comptime rd: u3) InstrFn {
|
||||||
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
fn inner(cpu: Arm32, opcode: u16) void {
|
fn inner(cpu: Arm32, bus: Bus, opcode: u16) void {
|
||||||
// LDR
|
// LDR
|
||||||
const offset = (opcode & 0xFF) << 2;
|
const offset = (opcode & 0xFF) << 2;
|
||||||
|
|
||||||
// Bit 1 of the PC intentionally ignored
|
// Bit 1 of the PC intentionally ignored
|
||||||
cpu.r[rd] = cpu.read(u32, (cpu.r[15] & ~@as(u32, 2)) + offset);
|
cpu.r[rd] = bus.read(u32, (cpu.r[15] & ~@as(u32, 2)) + offset);
|
||||||
}
|
}
|
||||||
}.inner;
|
}.inner;
|
||||||
}
|
}
|
||||||
|
@ -19,7 +21,7 @@ pub fn fmt78(comptime InstrFn: type, comptime op: u2, comptime T: bool) InstrFn
|
||||||
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
fn inner(cpu: Arm32, opcode: u16) void {
|
fn inner(cpu: Arm32, bus: Bus, opcode: u16) void {
|
||||||
const ro = opcode >> 6 & 0x7;
|
const ro = opcode >> 6 & 0x7;
|
||||||
const rb = opcode >> 3 & 0x7;
|
const rb = opcode >> 3 & 0x7;
|
||||||
const rd = opcode & 0x7;
|
const rd = opcode & 0x7;
|
||||||
|
@ -33,20 +35,20 @@ pub fn fmt78(comptime InstrFn: type, comptime op: u2, comptime T: bool) InstrFn
|
||||||
// STRH
|
// STRH
|
||||||
|
|
||||||
// FIXME: I shouldn't have to use @as(u8, ...) here
|
// FIXME: I shouldn't have to use @as(u8, ...) here
|
||||||
cpu.write(u16, address, @as(u16, @truncate(cpu.r[rd])));
|
bus.write(u16, address, @as(u16, @truncate(cpu.r[rd])));
|
||||||
},
|
},
|
||||||
0b01 => {
|
0b01 => {
|
||||||
// LDSB
|
// LDSB
|
||||||
cpu.r[rd] = sext(u32, u8, cpu.read(u8, address));
|
cpu.r[rd] = sext(u32, u8, bus.read(u8, address));
|
||||||
},
|
},
|
||||||
0b10 => {
|
0b10 => {
|
||||||
// LDRH
|
// LDRH
|
||||||
const value = cpu.read(u16, address);
|
const value = bus.read(u16, address);
|
||||||
cpu.r[rd] = rotr(u32, value, 8 * (address & 1));
|
cpu.r[rd] = rotr(u32, value, 8 * (address & 1));
|
||||||
},
|
},
|
||||||
0b11 => {
|
0b11 => {
|
||||||
// LDRSH
|
// LDRSH
|
||||||
const value = cpu.read(u16, address);
|
const value = bus.read(u16, address);
|
||||||
|
|
||||||
// FIXME: I shouldn't have to use @as(u8, ...) here
|
// FIXME: I shouldn't have to use @as(u8, ...) here
|
||||||
cpu.r[rd] = if (address & 1 == 1) sext(u32, u8, @as(u8, @truncate(value >> 8))) else sext(u32, u16, value);
|
cpu.r[rd] = if (address & 1 == 1) sext(u32, u8, @as(u8, @truncate(value >> 8))) else sext(u32, u16, value);
|
||||||
|
@ -57,22 +59,22 @@ pub fn fmt78(comptime InstrFn: type, comptime op: u2, comptime T: bool) InstrFn
|
||||||
switch (op) {
|
switch (op) {
|
||||||
0b00 => {
|
0b00 => {
|
||||||
// STR
|
// STR
|
||||||
cpu.write(u32, address, cpu.r[rd]);
|
bus.write(u32, address, cpu.r[rd]);
|
||||||
},
|
},
|
||||||
0b01 => {
|
0b01 => {
|
||||||
// STRB
|
// STRB
|
||||||
|
|
||||||
// FIXME: I shouldn't have to use @as(u8, ...) here
|
// FIXME: I shouldn't have to use @as(u8, ...) here
|
||||||
cpu.write(u8, address, @as(u8, @truncate(cpu.r[rd])));
|
bus.write(u8, address, @as(u8, @truncate(cpu.r[rd])));
|
||||||
},
|
},
|
||||||
0b10 => {
|
0b10 => {
|
||||||
// LDR
|
// LDR
|
||||||
const value = cpu.read(u32, address);
|
const value = bus.read(u32, address);
|
||||||
cpu.r[rd] = rotr(u32, value, 8 * (address & 0x3));
|
cpu.r[rd] = rotr(u32, value, 8 * (address & 0x3));
|
||||||
},
|
},
|
||||||
0b11 => {
|
0b11 => {
|
||||||
// LDRB
|
// LDRB
|
||||||
cpu.r[rd] = cpu.read(u8, address);
|
cpu.r[rd] = bus.read(u8, address);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -84,7 +86,7 @@ pub fn fmt9(comptime InstrFn: type, comptime B: bool, comptime L: bool, comptime
|
||||||
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
fn inner(cpu: Arm32, opcode: u16) void {
|
fn inner(cpu: Arm32, bus: Bus, opcode: u16) void {
|
||||||
const rb = opcode >> 3 & 0x7;
|
const rb = opcode >> 3 & 0x7;
|
||||||
const rd = opcode & 0x7;
|
const rd = opcode & 0x7;
|
||||||
|
|
||||||
|
@ -92,11 +94,11 @@ pub fn fmt9(comptime InstrFn: type, comptime B: bool, comptime L: bool, comptime
|
||||||
if (B) {
|
if (B) {
|
||||||
// LDRB
|
// LDRB
|
||||||
const address = cpu.r[rb] + offset;
|
const address = cpu.r[rb] + offset;
|
||||||
cpu.r[rd] = cpu.read(u8, address);
|
cpu.r[rd] = bus.read(u8, address);
|
||||||
} else {
|
} else {
|
||||||
// LDR
|
// LDR
|
||||||
const address = cpu.r[rb] + (@as(u32, offset) << 2);
|
const address = cpu.r[rb] + (@as(u32, offset) << 2);
|
||||||
const value = cpu.read(u32, address);
|
const value = bus.read(u32, address);
|
||||||
cpu.r[rd] = rotr(u32, value, 8 * (address & 0x3));
|
cpu.r[rd] = rotr(u32, value, 8 * (address & 0x3));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -105,11 +107,11 @@ pub fn fmt9(comptime InstrFn: type, comptime B: bool, comptime L: bool, comptime
|
||||||
const address = cpu.r[rb] + offset;
|
const address = cpu.r[rb] + offset;
|
||||||
|
|
||||||
// FIXME: I shouldn't have to use @as(u8, ...) here
|
// FIXME: I shouldn't have to use @as(u8, ...) here
|
||||||
cpu.write(u8, address, @as(u8, @truncate(cpu.r[rd])));
|
bus.write(u8, address, @as(u8, @truncate(cpu.r[rd])));
|
||||||
} else {
|
} else {
|
||||||
// STR
|
// STR
|
||||||
const address = cpu.r[rb] + (@as(u32, offset) << 2);
|
const address = cpu.r[rb] + (@as(u32, offset) << 2);
|
||||||
cpu.write(u32, address, cpu.r[rd]);
|
bus.write(u32, address, cpu.r[rd]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -120,7 +122,7 @@ pub fn fmt10(comptime InstrFn: type, comptime L: bool, comptime offset: u5) Inst
|
||||||
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
fn inner(cpu: Arm32, opcode: u16) void {
|
fn inner(cpu: Arm32, bus: Bus, opcode: u16) void {
|
||||||
const rb = opcode >> 3 & 0x7;
|
const rb = opcode >> 3 & 0x7;
|
||||||
const rd = opcode & 0x7;
|
const rd = opcode & 0x7;
|
||||||
|
|
||||||
|
@ -128,13 +130,13 @@ pub fn fmt10(comptime InstrFn: type, comptime L: bool, comptime offset: u5) Inst
|
||||||
|
|
||||||
if (L) {
|
if (L) {
|
||||||
// LDRH
|
// LDRH
|
||||||
const value = cpu.read(u16, address);
|
const value = bus.read(u16, address);
|
||||||
cpu.r[rd] = rotr(u32, value, 8 * (address & 1));
|
cpu.r[rd] = rotr(u32, value, 8 * (address & 1));
|
||||||
} else {
|
} else {
|
||||||
// STRH
|
// STRH
|
||||||
|
|
||||||
// FIXME: I shouldn't have to use @as(u8, ...) here
|
// FIXME: I shouldn't have to use @as(u8, ...) here
|
||||||
cpu.write(u16, address, @as(u16, @truncate(cpu.r[rd])));
|
bus.write(u16, address, @as(u16, @truncate(cpu.r[rd])));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.inner;
|
}.inner;
|
||||||
|
@ -144,17 +146,17 @@ pub fn fmt11(comptime InstrFn: type, comptime L: bool, comptime rd: u3) InstrFn
|
||||||
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
fn inner(cpu: Arm32, opcode: u16) void {
|
fn inner(cpu: Arm32, bus: Bus, opcode: u16) void {
|
||||||
const offset = (opcode & 0xFF) << 2;
|
const offset = (opcode & 0xFF) << 2;
|
||||||
const address = cpu.r[13] + offset;
|
const address = cpu.r[13] + offset;
|
||||||
|
|
||||||
if (L) {
|
if (L) {
|
||||||
// LDR
|
// LDR
|
||||||
const value = cpu.read(u32, address);
|
const value = bus.read(u32, address);
|
||||||
cpu.r[rd] = rotr(u32, value, 8 * (address & 0x3));
|
cpu.r[rd] = rotr(u32, value, 8 * (address & 0x3));
|
||||||
} else {
|
} else {
|
||||||
// STR
|
// STR
|
||||||
cpu.write(u32, address, cpu.r[rd]);
|
bus.write(u32, address, cpu.r[rd]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.inner;
|
}.inner;
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
|
const Bus = @import("../../../lib.zig").Bus;
|
||||||
|
|
||||||
pub fn fmt17(comptime InstrFn: type) InstrFn {
|
pub fn fmt17(comptime InstrFn: type) InstrFn {
|
||||||
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
const Arm32 = @typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?;
|
||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
fn inner(cpu: Arm32, _: u16) void {
|
fn inner(cpu: Arm32, _: Bus, _: u16) void {
|
||||||
// Copy Values from Current Mode
|
// Copy Values from Current Mode
|
||||||
const ret_addr = cpu.r[15] - 2;
|
const ret_addr = cpu.r[15] - 2;
|
||||||
const cpsr = cpu.cpsr.raw;
|
const cpsr = cpu.cpsr.raw;
|
||||||
|
|
404
src/arm/v4t.zig
404
src/arm/v4t.zig
|
@ -1,226 +1,230 @@
|
||||||
const Arm7tdmi = @import("../arm.zig").Arm32(.v4t);
|
const Bus = @import("../lib.zig").Bus;
|
||||||
|
|
||||||
pub const arm = struct {
|
pub fn arm(comptime Arm32: type) type {
|
||||||
pub const InstrFn = *const fn (*Arm7tdmi, u32) void;
|
return struct {
|
||||||
pub const lut: [0x1000]InstrFn = populate();
|
pub const InstrFn = *const fn (*Arm32, Bus, u32) void;
|
||||||
|
pub const lut: [0x1000]InstrFn = populate();
|
||||||
|
|
||||||
const processing = @import("cpu/arm/data_processing.zig").dataProcessing;
|
const processing = @import("cpu/arm/data_processing.zig").dataProcessing;
|
||||||
const psrTransfer = @import("cpu/arm/psr_transfer.zig").psrTransfer;
|
const psrTransfer = @import("cpu/arm/psr_transfer.zig").psrTransfer;
|
||||||
const transfer = @import("cpu/arm/single_data_transfer.zig").singleDataTransfer;
|
const transfer = @import("cpu/arm/single_data_transfer.zig").singleDataTransfer;
|
||||||
const halfSignedTransfer = @import("cpu/arm/half_signed_data_transfer.zig").halfAndSignedDataTransfer;
|
const halfSignedTransfer = @import("cpu/arm/half_signed_data_transfer.zig").halfAndSignedDataTransfer;
|
||||||
const blockTransfer = @import("cpu/arm/block_data_transfer.zig").blockDataTransfer;
|
const blockTransfer = @import("cpu/arm/block_data_transfer.zig").blockDataTransfer;
|
||||||
const branch = @import("cpu/arm/branch.zig").branch;
|
const branch = @import("cpu/arm/branch.zig").branch;
|
||||||
const branchExchange = @import("cpu/arm/branch.zig").branchAndExchange;
|
const branchExchange = @import("cpu/arm/branch.zig").branchAndExchange;
|
||||||
const swi = @import("cpu/arm/software_interrupt.zig").armSoftwareInterrupt;
|
const swi = @import("cpu/arm/software_interrupt.zig").armSoftwareInterrupt;
|
||||||
const swap = @import("cpu/arm/single_data_swap.zig").singleDataSwap;
|
const swap = @import("cpu/arm/single_data_swap.zig").singleDataSwap;
|
||||||
|
|
||||||
const multiply = @import("cpu/arm/multiply.zig").multiply;
|
const multiply = @import("cpu/arm/multiply.zig").multiply;
|
||||||
const multiplyLong = @import("cpu/arm/multiply.zig").multiplyLong;
|
const multiplyLong = @import("cpu/arm/multiply.zig").multiplyLong;
|
||||||
|
|
||||||
/// Determine index into ARM InstrFn LUT
|
/// Determine index into ARM InstrFn LUT
|
||||||
pub fn idx(opcode: u32) u12 {
|
pub fn idx(opcode: u32) u12 {
|
||||||
// FIXME: omit these?
|
// FIXME: omit these?
|
||||||
return @as(u12, @truncate(opcode >> 20 & 0xFF)) << 4 | @as(u12, @truncate(opcode >> 4 & 0xF));
|
return @as(u12, @truncate(opcode >> 20 & 0xFF)) << 4 | @as(u12, @truncate(opcode >> 4 & 0xF));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Undefined ARM Instruction handler
|
// Undefined ARM Instruction handler
|
||||||
fn und(cpu: *Arm7tdmi, opcode: u32) void {
|
fn und(cpu: *Arm32, _: Bus, opcode: u32) void {
|
||||||
const id = idx(opcode);
|
const id = idx(opcode);
|
||||||
cpu.panic("[CPU/Decode] ID: 0x{X:0>3} 0x{X:0>8} is an illegal opcode", .{ id, opcode });
|
cpu.panic("[CPU/Decode] ID: 0x{X:0>3} 0x{X:0>8} is an illegal opcode", .{ id, opcode });
|
||||||
}
|
}
|
||||||
|
|
||||||
fn populate() [0x1000]InstrFn {
|
fn populate() [0x1000]InstrFn {
|
||||||
return comptime comptime_blk: {
|
return comptime comptime_blk: {
|
||||||
@setEvalBranchQuota(0xE000);
|
@setEvalBranchQuota(0xE000);
|
||||||
var table = [_]InstrFn{und} ** 0x1000;
|
var table = [_]InstrFn{und} ** 0x1000;
|
||||||
|
|
||||||
for (&table, 0..) |*handler, i| {
|
for (&table, 0..) |*handler, i| {
|
||||||
handler.* = switch (@as(u2, i >> 10)) {
|
handler.* = switch (@as(u2, i >> 10)) {
|
||||||
0b00 => if (i == 0x121) blk: {
|
0b00 => if (i == 0x121) blk: {
|
||||||
break :blk branchExchange(InstrFn);
|
break :blk branchExchange(InstrFn);
|
||||||
} else if (i & 0xFCF == 0x009) blk: {
|
} else if (i & 0xFCF == 0x009) blk: {
|
||||||
const A = i >> 5 & 1 == 1;
|
const A = i >> 5 & 1 == 1;
|
||||||
const S = i >> 4 & 1 == 1;
|
const S = i >> 4 & 1 == 1;
|
||||||
break :blk multiply(InstrFn, A, S);
|
break :blk multiply(InstrFn, A, S);
|
||||||
} else if (i & 0xFBF == 0x109) blk: {
|
} else if (i & 0xFBF == 0x109) blk: {
|
||||||
const B = i >> 6 & 1 == 1;
|
const B = i >> 6 & 1 == 1;
|
||||||
break :blk swap(InstrFn, B);
|
break :blk swap(InstrFn, B);
|
||||||
} else if (i & 0xF8F == 0x089) blk: {
|
} else if (i & 0xF8F == 0x089) blk: {
|
||||||
const U = i >> 6 & 1 == 1;
|
const U = i >> 6 & 1 == 1;
|
||||||
const A = i >> 5 & 1 == 1;
|
const A = i >> 5 & 1 == 1;
|
||||||
const S = i >> 4 & 1 == 1;
|
const S = i >> 4 & 1 == 1;
|
||||||
break :blk multiplyLong(InstrFn, U, A, S);
|
break :blk multiplyLong(InstrFn, U, A, S);
|
||||||
} else if (i & 0xE49 == 0x009 or i & 0xE49 == 0x049) blk: {
|
} else if (i & 0xE49 == 0x009 or i & 0xE49 == 0x049) blk: {
|
||||||
const P = i >> 8 & 1 == 1;
|
|
||||||
const U = i >> 7 & 1 == 1;
|
|
||||||
const I = i >> 6 & 1 == 1;
|
|
||||||
const W = i >> 5 & 1 == 1;
|
|
||||||
const L = i >> 4 & 1 == 1;
|
|
||||||
break :blk halfSignedTransfer(InstrFn, P, U, I, W, L);
|
|
||||||
} else if (i & 0xD90 == 0x100) blk: {
|
|
||||||
const I = i >> 9 & 1 == 1;
|
|
||||||
const R = i >> 6 & 1 == 1;
|
|
||||||
const kind = i >> 4 & 0x3;
|
|
||||||
break :blk psrTransfer(InstrFn, I, R, kind);
|
|
||||||
} else blk: {
|
|
||||||
const I = i >> 9 & 1 == 1;
|
|
||||||
const S = i >> 4 & 1 == 1;
|
|
||||||
const instrKind = i >> 5 & 0xF;
|
|
||||||
break :blk processing(InstrFn, I, S, instrKind);
|
|
||||||
},
|
|
||||||
0b01 => if (i >> 9 & 1 == 1 and i & 1 == 1) und else blk: {
|
|
||||||
const I = i >> 9 & 1 == 1;
|
|
||||||
const P = i >> 8 & 1 == 1;
|
|
||||||
const U = i >> 7 & 1 == 1;
|
|
||||||
const B = i >> 6 & 1 == 1;
|
|
||||||
const W = i >> 5 & 1 == 1;
|
|
||||||
const L = i >> 4 & 1 == 1;
|
|
||||||
break :blk transfer(InstrFn, I, P, U, B, W, L);
|
|
||||||
},
|
|
||||||
else => switch (@as(u2, i >> 9 & 0x3)) {
|
|
||||||
// MSB is guaranteed to be 1
|
|
||||||
0b00 => blk: {
|
|
||||||
const P = i >> 8 & 1 == 1;
|
const P = i >> 8 & 1 == 1;
|
||||||
const U = i >> 7 & 1 == 1;
|
const U = i >> 7 & 1 == 1;
|
||||||
const S = i >> 6 & 1 == 1;
|
const I = i >> 6 & 1 == 1;
|
||||||
const W = i >> 5 & 1 == 1;
|
const W = i >> 5 & 1 == 1;
|
||||||
const L = i >> 4 & 1 == 1;
|
const L = i >> 4 & 1 == 1;
|
||||||
break :blk blockTransfer(InstrFn, P, U, S, W, L);
|
break :blk halfSignedTransfer(InstrFn, P, U, I, W, L);
|
||||||
},
|
} else if (i & 0xD90 == 0x100) blk: {
|
||||||
0b01 => blk: {
|
const I = i >> 9 & 1 == 1;
|
||||||
const L = i >> 8 & 1 == 1;
|
const R = i >> 6 & 1 == 1;
|
||||||
break :blk branch(InstrFn, L);
|
const kind = i >> 4 & 0x3;
|
||||||
},
|
break :blk psrTransfer(InstrFn, I, R, kind);
|
||||||
0b10 => und, // COP Data Transfer
|
|
||||||
0b11 => if (i >> 8 & 1 == 1) swi(InstrFn) else und, // COP Data Operation + Register Transfer
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
break :comptime_blk table;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const thumb = struct {
|
|
||||||
pub const InstrFn = *const fn (*Arm7tdmi, u16) void;
|
|
||||||
pub const lut: [0x400]InstrFn = populate();
|
|
||||||
|
|
||||||
const processing = @import("cpu/thumb/data_processing.zig");
|
|
||||||
const alu = @import("cpu/thumb/alu.zig").fmt4;
|
|
||||||
const transfer = @import("cpu/thumb/data_transfer.zig");
|
|
||||||
const block_transfer = @import("cpu/thumb/block_data_transfer.zig");
|
|
||||||
const swi = @import("cpu/thumb/software_interrupt.zig").fmt17;
|
|
||||||
const branch = @import("cpu/thumb/branch.zig");
|
|
||||||
|
|
||||||
/// Determine index into THUMB InstrFn LUT
|
|
||||||
pub fn idx(opcode: u16) u10 {
|
|
||||||
return @truncate(opcode >> 6);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Undefined THUMB Instruction Handler
|
|
||||||
fn und(cpu: *Arm7tdmi, opcode: u16) void {
|
|
||||||
const id = idx(opcode);
|
|
||||||
cpu.panic("[CPU/Decode] ID: 0b{b:0>10} 0x{X:0>2} is an illegal opcode", .{ id, opcode });
|
|
||||||
}
|
|
||||||
|
|
||||||
fn populate() [0x400]InstrFn {
|
|
||||||
return comptime comptime_blk: {
|
|
||||||
@setEvalBranchQuota(5025); // This is exact
|
|
||||||
var table = [_]InstrFn{und} ** 0x400;
|
|
||||||
|
|
||||||
for (&table, 0..) |*handler, i| {
|
|
||||||
handler.* = switch (@as(u3, i >> 7 & 0x7)) {
|
|
||||||
0b000 => if (i >> 5 & 0x3 == 0b11) blk: {
|
|
||||||
const I = i >> 4 & 1 == 1;
|
|
||||||
const is_sub = i >> 3 & 1 == 1;
|
|
||||||
const rn = i & 0x7;
|
|
||||||
break :blk processing.fmt2(InstrFn, I, is_sub, rn);
|
|
||||||
} else blk: {
|
|
||||||
const op = i >> 5 & 0x3;
|
|
||||||
const offset = i & 0x1F;
|
|
||||||
break :blk processing.fmt1(InstrFn, op, offset);
|
|
||||||
},
|
|
||||||
0b001 => blk: {
|
|
||||||
const op = i >> 5 & 0x3;
|
|
||||||
const rd = i >> 2 & 0x7;
|
|
||||||
break :blk processing.fmt3(InstrFn, op, rd);
|
|
||||||
},
|
|
||||||
0b010 => switch (@as(u2, i >> 5 & 0x3)) {
|
|
||||||
0b00 => if (i >> 4 & 1 == 1) blk: {
|
|
||||||
const op = i >> 2 & 0x3;
|
|
||||||
const h1 = i >> 1 & 1;
|
|
||||||
const h2 = i & 1;
|
|
||||||
break :blk processing.fmt5(InstrFn, op, h1, h2);
|
|
||||||
} else blk: {
|
} else blk: {
|
||||||
const op = i & 0xF;
|
const I = i >> 9 & 1 == 1;
|
||||||
break :blk alu(InstrFn, op);
|
const S = i >> 4 & 1 == 1;
|
||||||
|
const instrKind = i >> 5 & 0xF;
|
||||||
|
break :blk processing(InstrFn, I, S, instrKind);
|
||||||
},
|
},
|
||||||
0b01 => blk: {
|
0b01 => if (i >> 9 & 1 == 1 and i & 1 == 1) und else blk: {
|
||||||
const rd = i >> 2 & 0x7;
|
const I = i >> 9 & 1 == 1;
|
||||||
break :blk transfer.fmt6(InstrFn, rd);
|
const P = i >> 8 & 1 == 1;
|
||||||
|
const U = i >> 7 & 1 == 1;
|
||||||
|
const B = i >> 6 & 1 == 1;
|
||||||
|
const W = i >> 5 & 1 == 1;
|
||||||
|
const L = i >> 4 & 1 == 1;
|
||||||
|
break :blk transfer(InstrFn, I, P, U, B, W, L);
|
||||||
},
|
},
|
||||||
else => blk: {
|
else => switch (@as(u2, i >> 9 & 0x3)) {
|
||||||
const op = i >> 4 & 0x3;
|
// MSB is guaranteed to be 1
|
||||||
const T = i >> 3 & 1 == 1;
|
0b00 => blk: {
|
||||||
break :blk transfer.fmt78(InstrFn, op, T);
|
const P = i >> 8 & 1 == 1;
|
||||||
|
const U = i >> 7 & 1 == 1;
|
||||||
|
const S = i >> 6 & 1 == 1;
|
||||||
|
const W = i >> 5 & 1 == 1;
|
||||||
|
const L = i >> 4 & 1 == 1;
|
||||||
|
break :blk blockTransfer(InstrFn, P, U, S, W, L);
|
||||||
|
},
|
||||||
|
0b01 => blk: {
|
||||||
|
const L = i >> 8 & 1 == 1;
|
||||||
|
break :blk branch(InstrFn, L);
|
||||||
|
},
|
||||||
|
0b10 => und, // COP Data Transfer
|
||||||
|
0b11 => if (i >> 8 & 1 == 1) swi(InstrFn) else und, // COP Data Operation + Register Transfer
|
||||||
},
|
},
|
||||||
},
|
};
|
||||||
0b011 => blk: {
|
}
|
||||||
const B = i >> 6 & 1 == 1;
|
|
||||||
const L = i >> 5 & 1 == 1;
|
break :comptime_blk table;
|
||||||
const offset = i & 0x1F;
|
};
|
||||||
break :blk transfer.fmt9(InstrFn, B, L, offset);
|
}
|
||||||
},
|
};
|
||||||
else => switch (@as(u3, i >> 6 & 0x7)) {
|
}
|
||||||
// MSB is guaranteed to be 1
|
|
||||||
0b000 => blk: {
|
pub fn thumb(comptime Arm32: type) type {
|
||||||
const L = i >> 5 & 1 == 1;
|
return struct {
|
||||||
|
pub const InstrFn = *const fn (*Arm32, Bus, u16) void;
|
||||||
|
pub const lut: [0x400]InstrFn = populate();
|
||||||
|
|
||||||
|
const processing = @import("cpu/thumb/data_processing.zig");
|
||||||
|
const alu = @import("cpu/thumb/alu.zig").fmt4;
|
||||||
|
const transfer = @import("cpu/thumb/data_transfer.zig");
|
||||||
|
const block_transfer = @import("cpu/thumb/block_data_transfer.zig");
|
||||||
|
const swi = @import("cpu/thumb/software_interrupt.zig").fmt17;
|
||||||
|
const branch = @import("cpu/thumb/branch.zig");
|
||||||
|
|
||||||
|
/// Determine index into THUMB InstrFn LUT
|
||||||
|
pub fn idx(opcode: u16) u10 {
|
||||||
|
return @truncate(opcode >> 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Undefined THUMB Instruction Handler
|
||||||
|
fn und(cpu: *Arm32, _: Bus, opcode: u16) void {
|
||||||
|
const id = idx(opcode);
|
||||||
|
cpu.panic("[CPU/Decode] ID: 0b{b:0>10} 0x{X:0>2} is an illegal opcode", .{ id, opcode });
|
||||||
|
}
|
||||||
|
|
||||||
|
fn populate() [0x400]InstrFn {
|
||||||
|
return comptime comptime_blk: {
|
||||||
|
@setEvalBranchQuota(5025); // This is exact
|
||||||
|
var table = [_]InstrFn{und} ** 0x400;
|
||||||
|
|
||||||
|
for (&table, 0..) |*handler, i| {
|
||||||
|
handler.* = switch (@as(u3, i >> 7 & 0x7)) {
|
||||||
|
0b000 => if (i >> 5 & 0x3 == 0b11) blk: {
|
||||||
|
const I = i >> 4 & 1 == 1;
|
||||||
|
const is_sub = i >> 3 & 1 == 1;
|
||||||
|
const rn = i & 0x7;
|
||||||
|
break :blk processing.fmt2(InstrFn, I, is_sub, rn);
|
||||||
|
} else blk: {
|
||||||
|
const op = i >> 5 & 0x3;
|
||||||
const offset = i & 0x1F;
|
const offset = i & 0x1F;
|
||||||
break :blk transfer.fmt10(InstrFn, L, offset);
|
break :blk processing.fmt1(InstrFn, op, offset);
|
||||||
},
|
},
|
||||||
0b001 => blk: {
|
0b001 => blk: {
|
||||||
const L = i >> 5 & 1 == 1;
|
const op = i >> 5 & 0x3;
|
||||||
const rd = i >> 2 & 0x7;
|
const rd = i >> 2 & 0x7;
|
||||||
break :blk transfer.fmt11(InstrFn, L, rd);
|
break :blk processing.fmt3(InstrFn, op, rd);
|
||||||
},
|
},
|
||||||
0b010 => blk: {
|
0b010 => switch (@as(u2, i >> 5 & 0x3)) {
|
||||||
const isSP = i >> 5 & 1 == 1;
|
0b00 => if (i >> 4 & 1 == 1) blk: {
|
||||||
const rd = i >> 2 & 0x7;
|
const op = i >> 2 & 0x3;
|
||||||
break :blk processing.fmt12(InstrFn, isSP, rd);
|
const h1 = i >> 1 & 1;
|
||||||
|
const h2 = i & 1;
|
||||||
|
break :blk processing.fmt5(InstrFn, op, h1, h2);
|
||||||
|
} else blk: {
|
||||||
|
const op = i & 0xF;
|
||||||
|
break :blk alu(InstrFn, op);
|
||||||
|
},
|
||||||
|
0b01 => blk: {
|
||||||
|
const rd = i >> 2 & 0x7;
|
||||||
|
break :blk transfer.fmt6(InstrFn, rd);
|
||||||
|
},
|
||||||
|
else => blk: {
|
||||||
|
const op = i >> 4 & 0x3;
|
||||||
|
const T = i >> 3 & 1 == 1;
|
||||||
|
break :blk transfer.fmt78(InstrFn, op, T);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
0b011 => if (i >> 4 & 1 == 1) blk: {
|
0b011 => blk: {
|
||||||
|
const B = i >> 6 & 1 == 1;
|
||||||
const L = i >> 5 & 1 == 1;
|
const L = i >> 5 & 1 == 1;
|
||||||
const R = i >> 2 & 1 == 1;
|
const offset = i & 0x1F;
|
||||||
break :blk block_transfer.fmt14(InstrFn, L, R);
|
break :blk transfer.fmt9(InstrFn, B, L, offset);
|
||||||
} else blk: {
|
|
||||||
const S = i >> 1 & 1 == 1;
|
|
||||||
break :blk processing.fmt13(InstrFn, S);
|
|
||||||
},
|
},
|
||||||
0b100 => blk: {
|
else => switch (@as(u3, i >> 6 & 0x7)) {
|
||||||
const L = i >> 5 & 1 == 1;
|
// MSB is guaranteed to be 1
|
||||||
const rb = i >> 2 & 0x7;
|
0b000 => blk: {
|
||||||
|
const L = i >> 5 & 1 == 1;
|
||||||
|
const offset = i & 0x1F;
|
||||||
|
break :blk transfer.fmt10(InstrFn, L, offset);
|
||||||
|
},
|
||||||
|
0b001 => blk: {
|
||||||
|
const L = i >> 5 & 1 == 1;
|
||||||
|
const rd = i >> 2 & 0x7;
|
||||||
|
break :blk transfer.fmt11(InstrFn, L, rd);
|
||||||
|
},
|
||||||
|
0b010 => blk: {
|
||||||
|
const isSP = i >> 5 & 1 == 1;
|
||||||
|
const rd = i >> 2 & 0x7;
|
||||||
|
break :blk processing.fmt12(InstrFn, isSP, rd);
|
||||||
|
},
|
||||||
|
0b011 => if (i >> 4 & 1 == 1) blk: {
|
||||||
|
const L = i >> 5 & 1 == 1;
|
||||||
|
const R = i >> 2 & 1 == 1;
|
||||||
|
break :blk block_transfer.fmt14(InstrFn, L, R);
|
||||||
|
} else blk: {
|
||||||
|
const S = i >> 1 & 1 == 1;
|
||||||
|
break :blk processing.fmt13(InstrFn, S);
|
||||||
|
},
|
||||||
|
0b100 => blk: {
|
||||||
|
const L = i >> 5 & 1 == 1;
|
||||||
|
const rb = i >> 2 & 0x7;
|
||||||
|
|
||||||
break :blk block_transfer.fmt15(InstrFn, L, rb);
|
break :blk block_transfer.fmt15(InstrFn, L, rb);
|
||||||
|
},
|
||||||
|
0b101 => if (i >> 2 & 0xF == 0b1111) blk: {
|
||||||
|
break :blk swi(InstrFn);
|
||||||
|
} else blk: {
|
||||||
|
const cond = i >> 2 & 0xF;
|
||||||
|
break :blk branch.fmt16(InstrFn, cond);
|
||||||
|
},
|
||||||
|
0b110 => branch.fmt18(
|
||||||
|
InstrFn,
|
||||||
|
),
|
||||||
|
0b111 => blk: {
|
||||||
|
const is_low = i >> 5 & 1 == 1;
|
||||||
|
break :blk branch.fmt19(InstrFn, is_low);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
0b101 => if (i >> 2 & 0xF == 0b1111) blk: {
|
};
|
||||||
break :blk swi(InstrFn);
|
}
|
||||||
} else blk: {
|
|
||||||
const cond = i >> 2 & 0xF;
|
|
||||||
break :blk branch.fmt16(InstrFn, cond);
|
|
||||||
},
|
|
||||||
0b110 => branch.fmt18(
|
|
||||||
InstrFn,
|
|
||||||
),
|
|
||||||
0b111 => blk: {
|
|
||||||
const is_low = i >> 5 & 1 == 1;
|
|
||||||
break :blk branch.fmt19(InstrFn, is_low);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
break :comptime_blk table;
|
break :comptime_blk table;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
249
src/arm/v5te.zig
249
src/arm/v5te.zig
|
@ -1,249 +0,0 @@
|
||||||
const Arm946es = @import("../arm.zig").Arm32(.v5te);
|
|
||||||
|
|
||||||
pub const arm = struct {
|
|
||||||
pub const InstrFn = *const fn (*Arm946es, u32) void;
|
|
||||||
pub const lut: [0x1000]InstrFn = populate();
|
|
||||||
|
|
||||||
const processing = @import("cpu/arm/data_processing.zig").dataProcessing;
|
|
||||||
const psrTransfer = @import("cpu/arm/psr_transfer.zig").psrTransfer;
|
|
||||||
const transfer = @import("cpu/arm/single_data_transfer.zig").singleDataTransfer;
|
|
||||||
const halfSignedTransfer = @import("cpu/arm/half_signed_data_transfer.zig").halfAndSignedDataTransfer;
|
|
||||||
const blockTransfer = @import("cpu/arm/block_data_transfer.zig").blockDataTransfer;
|
|
||||||
const branch = @import("cpu/arm/branch.zig").branch;
|
|
||||||
const branchExchange = @import("cpu/arm/branch.zig").branchAndExchange;
|
|
||||||
const swi = @import("cpu/arm/software_interrupt.zig").armSoftwareInterrupt;
|
|
||||||
const swap = @import("cpu/arm/single_data_swap.zig").singleDataSwap;
|
|
||||||
|
|
||||||
const multiply = @import("cpu/arm/multiply.zig").multiply;
|
|
||||||
const multiplyLong = @import("cpu/arm/multiply.zig").multiplyLong;
|
|
||||||
|
|
||||||
const cop = @import("cpu/arm/coprocessor.zig");
|
|
||||||
|
|
||||||
/// Determine index into ARM InstrFn LUT
|
|
||||||
pub fn idx(opcode: u32) u12 {
|
|
||||||
// FIXME: omit these?
|
|
||||||
return @as(u12, @truncate(opcode >> 20 & 0xFF)) << 4 | @as(u12, @truncate(opcode >> 4 & 0xF));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Undefined ARM Instruction handler
|
|
||||||
fn und(cpu: *Arm946es, opcode: u32) void {
|
|
||||||
const id = idx(opcode);
|
|
||||||
cpu.panic("[CPU/Decode] ID: 0x{X:0>3} 0x{X:0>8} is an illegal opcode", .{ id, opcode });
|
|
||||||
}
|
|
||||||
|
|
||||||
fn populate() [0x1000]InstrFn {
|
|
||||||
return comptime comptime_blk: {
|
|
||||||
@setEvalBranchQuota(0xE000);
|
|
||||||
var table = [_]InstrFn{und} ** 0x1000;
|
|
||||||
|
|
||||||
for (&table, 0..) |*handler, i| {
|
|
||||||
handler.* = switch (@as(u2, i >> 10)) {
|
|
||||||
0b00 => if (i == 0x121) blk: {
|
|
||||||
break :blk branchExchange(InstrFn);
|
|
||||||
} else if (i & 0xFCF == 0x009) blk: {
|
|
||||||
const A = i >> 5 & 1 == 1;
|
|
||||||
const S = i >> 4 & 1 == 1;
|
|
||||||
break :blk multiply(InstrFn, A, S);
|
|
||||||
} else if (i & 0xFBF == 0x109) blk: {
|
|
||||||
const B = i >> 6 & 1 == 1;
|
|
||||||
break :blk swap(InstrFn, B);
|
|
||||||
} else if (i & 0xF8F == 0x089) blk: {
|
|
||||||
const U = i >> 6 & 1 == 1;
|
|
||||||
const A = i >> 5 & 1 == 1;
|
|
||||||
const S = i >> 4 & 1 == 1;
|
|
||||||
break :blk multiplyLong(InstrFn, U, A, S);
|
|
||||||
} else if (i & 0xE49 == 0x009 or i & 0xE49 == 0x049) blk: {
|
|
||||||
const P = i >> 8 & 1 == 1;
|
|
||||||
const U = i >> 7 & 1 == 1;
|
|
||||||
const I = i >> 6 & 1 == 1;
|
|
||||||
const W = i >> 5 & 1 == 1;
|
|
||||||
const L = i >> 4 & 1 == 1;
|
|
||||||
break :blk halfSignedTransfer(InstrFn, P, U, I, W, L);
|
|
||||||
} else if (i & 0xD90 == 0x100) blk: {
|
|
||||||
const I = i >> 9 & 1 == 1;
|
|
||||||
const R = i >> 6 & 1 == 1;
|
|
||||||
const kind = i >> 4 & 0x3;
|
|
||||||
break :blk psrTransfer(InstrFn, I, R, kind);
|
|
||||||
} else blk: {
|
|
||||||
const I = i >> 9 & 1 == 1;
|
|
||||||
const S = i >> 4 & 1 == 1;
|
|
||||||
const instrKind = i >> 5 & 0xF;
|
|
||||||
break :blk processing(InstrFn, I, S, instrKind);
|
|
||||||
},
|
|
||||||
0b01 => if (i >> 9 & 1 == 1 and i & 1 == 1) und else blk: {
|
|
||||||
const I = i >> 9 & 1 == 1;
|
|
||||||
const P = i >> 8 & 1 == 1;
|
|
||||||
const U = i >> 7 & 1 == 1;
|
|
||||||
const B = i >> 6 & 1 == 1;
|
|
||||||
const W = i >> 5 & 1 == 1;
|
|
||||||
const L = i >> 4 & 1 == 1;
|
|
||||||
break :blk transfer(InstrFn, I, P, U, B, W, L);
|
|
||||||
},
|
|
||||||
else => switch (@as(u2, i >> 9 & 0x3)) {
|
|
||||||
// MSB is guaranteed to be 1
|
|
||||||
0b00 => blk: {
|
|
||||||
const P = i >> 8 & 1 == 1;
|
|
||||||
const U = i >> 7 & 1 == 1;
|
|
||||||
const S = i >> 6 & 1 == 1;
|
|
||||||
const W = i >> 5 & 1 == 1;
|
|
||||||
const L = i >> 4 & 1 == 1;
|
|
||||||
break :blk blockTransfer(InstrFn, P, U, S, W, L);
|
|
||||||
},
|
|
||||||
0b01 => blk: {
|
|
||||||
const L = i >> 8 & 1 == 1;
|
|
||||||
break :blk branch(InstrFn, L);
|
|
||||||
},
|
|
||||||
0b10 => blk: {
|
|
||||||
const P = i >> 8 & 1 == 1;
|
|
||||||
const U = i >> 7 & 1 == 1;
|
|
||||||
const N = i >> 6 & 1 == 1;
|
|
||||||
const W = i >> 5 & 1 == 1;
|
|
||||||
const L = i >> 4 & 1 == 1;
|
|
||||||
|
|
||||||
break :blk cop.dataTransfer(InstrFn, P, U, N, W, L);
|
|
||||||
},
|
|
||||||
0b11 => blk: {
|
|
||||||
if (i >> 8 & 1 == 1) break :blk swi(InstrFn);
|
|
||||||
|
|
||||||
const data_opcode1 = i >> 4 & 0xF; // bits 20 -> 23
|
|
||||||
const reg_opcode1 = i >> 5 & 0x7; // bits 21 -> 23
|
|
||||||
const opcode2 = i >> 1 & 0x7; // bits 5 -> 7
|
|
||||||
const L = i >> 4 & 1 == 1; // bit 20
|
|
||||||
|
|
||||||
// Bit 4 (index pos of 0) distinguishes between these classes of instructions
|
|
||||||
break :blk switch (i & 1 == 1) {
|
|
||||||
true => cop.registerTransfer(InstrFn, reg_opcode1, L, opcode2),
|
|
||||||
false => cop.dataProcessing(InstrFn, data_opcode1, opcode2),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
break :comptime_blk table;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const thumb = struct {
|
|
||||||
pub const InstrFn = *const fn (*Arm946es, u16) void;
|
|
||||||
pub const lut: [0x400]InstrFn = populate();
|
|
||||||
|
|
||||||
const processing = @import("cpu/thumb/data_processing.zig");
|
|
||||||
const alu = @import("cpu/thumb/alu.zig").fmt4;
|
|
||||||
const transfer = @import("cpu/thumb/data_transfer.zig");
|
|
||||||
const block_transfer = @import("cpu/thumb/block_data_transfer.zig");
|
|
||||||
const swi = @import("cpu/thumb/software_interrupt.zig").fmt17;
|
|
||||||
const branch = @import("cpu/thumb/branch.zig");
|
|
||||||
|
|
||||||
/// Determine index into THUMB InstrFn LUT
|
|
||||||
pub fn idx(opcode: u16) u10 {
|
|
||||||
return @truncate(opcode >> 6);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Undefined THUMB Instruction Handler
|
|
||||||
fn und(cpu: *Arm946es, opcode: u16) void {
|
|
||||||
const id = idx(opcode);
|
|
||||||
cpu.panic("[CPU/Decode] ID: 0b{b:0>10} 0x{X:0>2} is an illegal opcode", .{ id, opcode });
|
|
||||||
}
|
|
||||||
|
|
||||||
fn populate() [0x400]InstrFn {
|
|
||||||
return comptime comptime_blk: {
|
|
||||||
@setEvalBranchQuota(5025); // This is exact
|
|
||||||
var table = [_]InstrFn{und} ** 0x400;
|
|
||||||
|
|
||||||
for (&table, 0..) |*handler, i| {
|
|
||||||
handler.* = switch (@as(u3, i >> 7 & 0x7)) {
|
|
||||||
0b000 => if (i >> 5 & 0x3 == 0b11) blk: {
|
|
||||||
const I = i >> 4 & 1 == 1;
|
|
||||||
const is_sub = i >> 3 & 1 == 1;
|
|
||||||
const rn = i & 0x7;
|
|
||||||
break :blk processing.fmt2(InstrFn, I, is_sub, rn);
|
|
||||||
} else blk: {
|
|
||||||
const op = i >> 5 & 0x3;
|
|
||||||
const offset = i & 0x1F;
|
|
||||||
break :blk processing.fmt1(InstrFn, op, offset);
|
|
||||||
},
|
|
||||||
0b001 => blk: {
|
|
||||||
const op = i >> 5 & 0x3;
|
|
||||||
const rd = i >> 2 & 0x7;
|
|
||||||
break :blk processing.fmt3(InstrFn, op, rd);
|
|
||||||
},
|
|
||||||
0b010 => switch (@as(u2, i >> 5 & 0x3)) {
|
|
||||||
0b00 => if (i >> 4 & 1 == 1) blk: {
|
|
||||||
const op = i >> 2 & 0x3;
|
|
||||||
const h1 = i >> 1 & 1;
|
|
||||||
const h2 = i & 1;
|
|
||||||
break :blk processing.fmt5(InstrFn, op, h1, h2);
|
|
||||||
} else blk: {
|
|
||||||
const op = i & 0xF;
|
|
||||||
break :blk alu(InstrFn, op);
|
|
||||||
},
|
|
||||||
0b01 => blk: {
|
|
||||||
const rd = i >> 2 & 0x7;
|
|
||||||
break :blk transfer.fmt6(InstrFn, rd);
|
|
||||||
},
|
|
||||||
else => blk: {
|
|
||||||
const op = i >> 4 & 0x3;
|
|
||||||
const T = i >> 3 & 1 == 1;
|
|
||||||
break :blk transfer.fmt78(InstrFn, op, T);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
0b011 => blk: {
|
|
||||||
const B = i >> 6 & 1 == 1;
|
|
||||||
const L = i >> 5 & 1 == 1;
|
|
||||||
const offset = i & 0x1F;
|
|
||||||
break :blk transfer.fmt9(InstrFn, B, L, offset);
|
|
||||||
},
|
|
||||||
else => switch (@as(u3, i >> 6 & 0x7)) {
|
|
||||||
// MSB is guaranteed to be 1
|
|
||||||
0b000 => blk: {
|
|
||||||
const L = i >> 5 & 1 == 1;
|
|
||||||
const offset = i & 0x1F;
|
|
||||||
break :blk transfer.fmt10(InstrFn, L, offset);
|
|
||||||
},
|
|
||||||
0b001 => blk: {
|
|
||||||
const L = i >> 5 & 1 == 1;
|
|
||||||
const rd = i >> 2 & 0x7;
|
|
||||||
break :blk transfer.fmt11(InstrFn, L, rd);
|
|
||||||
},
|
|
||||||
0b010 => blk: {
|
|
||||||
const isSP = i >> 5 & 1 == 1;
|
|
||||||
const rd = i >> 2 & 0x7;
|
|
||||||
break :blk processing.fmt12(InstrFn, isSP, rd);
|
|
||||||
},
|
|
||||||
0b011 => if (i >> 4 & 1 == 1) blk: {
|
|
||||||
const L = i >> 5 & 1 == 1;
|
|
||||||
const R = i >> 2 & 1 == 1;
|
|
||||||
break :blk block_transfer.fmt14(InstrFn, L, R);
|
|
||||||
} else blk: {
|
|
||||||
const S = i >> 1 & 1 == 1;
|
|
||||||
break :blk processing.fmt13(InstrFn, S);
|
|
||||||
},
|
|
||||||
0b100 => blk: {
|
|
||||||
const L = i >> 5 & 1 == 1;
|
|
||||||
const rb = i >> 2 & 0x7;
|
|
||||||
|
|
||||||
break :blk block_transfer.fmt15(InstrFn, L, rb);
|
|
||||||
},
|
|
||||||
0b101 => if (i >> 2 & 0xF == 0b1111) blk: {
|
|
||||||
break :blk swi(InstrFn);
|
|
||||||
} else blk: {
|
|
||||||
const cond = i >> 2 & 0xF;
|
|
||||||
break :blk branch.fmt16(InstrFn, cond);
|
|
||||||
},
|
|
||||||
0b110 => branch.fmt18(
|
|
||||||
InstrFn,
|
|
||||||
),
|
|
||||||
0b111 => blk: {
|
|
||||||
const is_low = i >> 5 & 1 == 1;
|
|
||||||
break :blk branch.fmt19(InstrFn, is_low);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
break :comptime_blk table;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
Loading…
Reference in New Issue