feat: pass nds arm7wrestler

- impl behaviour of running v5te instrs on v4t cpu
- impl undefined instruction exception handler
- panic on what I think are still unimplemented v5te opcodes
This commit is contained in:
Rekai Nyangadzayi Musuka 2023-12-27 00:11:42 -06:00
parent dcff3fd588
commit aad3bdc9ea
5 changed files with 66 additions and 13 deletions

View File

@ -409,6 +409,29 @@ pub fn Arm32(comptime isa: Architecture) type {
std.debug.panic(format, args); std.debug.panic(format, args);
} }
// TODO: Rename
pub fn undefinedInstructionTrap(self: *Self) void {
// Copy Values from Current Mode
const ret_addr = self.r[15] - @as(u32, if (self.cpsr.t.read()) 2 else 4);
const cpsr = self.cpsr.raw;
// Switch Mode
self.changeMode(.Undefined);
self.cpsr.t.write(false); // Force ARM Mode
self.cpsr.i.write(true); // Disable normal interrupts
self.r[14] = ret_addr; // Resume Execution
self.spsr.raw = cpsr; // Previous mode CPSR
self.r[15] = switch (Self.arch) {
.v4t => 0x0000_0004,
.v5te => blk: {
const ctrl = self.cp15.read(0, 1, 0, 0);
break :blk if (ctrl >> 13 & 1 == 1) 0xFFFF_0004 else 0x0000_0004;
},
};
self.pipe.reload(self);
}
pub fn interface(self: *Self) Interpreter { pub fn interface(self: *Self) Interpreter {
return switch (isa) { return switch (isa) {
.v4t => .{ .v4t = self }, .v4t => .{ .v4t = self },

View File

@ -51,14 +51,13 @@ pub fn dataTransfer(
// TODO: Increment address + 4 (and perform op) until coprocessor says stop // TODO: Increment address + 4 (and perform op) until coprocessor says stop
if (L) { if (L) {
log.debug("TODO: ldc{s} p{}, c{}, 0x{X:0>8}", .{ [_]u8{if (N) 'l' else ' '}, cp_num, crd, start_address }); cpu.panic("TODO: ldc{s} p{}, c{}, 0x{X:0>8}", .{ [_]u8{if (N) 'l' else ' '}, cp_num, crd, start_address });
} else { } else {
log.debug("TODO: stc{s} p{}, c{}, 0x{X:0>8}", .{ [_]u8{if (N) 'l' else ' '}, cp_num, crd, start_address }); cpu.panic("TODO: stc{s} p{}, c{}, 0x{X:0>8}", .{ [_]u8{if (N) 'l' else ' '}, cp_num, crd, start_address });
} }
} }
fn copExt(cpu: *Arm32, opcode: u32) void { fn copExt(cpu: *Arm32, opcode: u32) void {
_ = cpu;
const cp_num = opcode >> 8 & 0xF; const cp_num = opcode >> 8 & 0xF;
const rd = opcode >> 12 & 0xF; const rd = opcode >> 12 & 0xF;
const rn = opcode >> 16 & 0xF; const rn = opcode >> 16 & 0xF;
@ -71,10 +70,10 @@ pub fn dataTransfer(
if (L) { if (L) {
// MRRC // MRRC
log.debug("TODO: mrrc p{}, {}, r{}, r{}, c{}", .{ cp_num, cp_opcode, rd, rn, crm }); cpu.panic("TODO: mrrc p{}, {}, r{}, r{}, c{}", .{ cp_num, cp_opcode, rd, rn, crm });
} else { } else {
// MCRR // MCRR
log.debug("TODO: mcrr p{}, {}, r{}, r{}, c{}", .{ cp_num, cp_opcode, rd, rn, crm }); cpu.panic("TODO: mcrr p{}, {}, r{}, r{}, c{}", .{ cp_num, cp_opcode, rd, rn, crm });
} }
} }
}.inner; }.inner;
@ -90,7 +89,11 @@ pub fn registerTransfer(comptime InstrFn: type, comptime opcode1: u3, comptime L
const cp_num = opcode >> 8 & 0xF; const cp_num = opcode >> 8 & 0xF;
const crm: u4 = @intCast(opcode & 0xF); const crm: u4 = @intCast(opcode & 0xF);
std.debug.assert(cp_num == 0xF); // There's no other coprocessor on NDS9; switch (cp_num) {
14 => return,
15 => if (Arm32.arch == .v4t) return cpu.undefinedInstructionTrap(),
else => cpu.panic("MRC: unexpected coprocessor #: {}", .{cp_num}),
}
if (L) { if (L) {
// MRC // MRC
@ -148,9 +151,7 @@ pub fn dataProcessing(comptime InstrFn: type, comptime opcode1: u4, comptime opc
return struct { return struct {
fn inner(cpu: *Arm32, opcode: u32) void { fn inner(cpu: *Arm32, opcode: u32) void {
_ = cpu; cpu.panic("TODO: handle 0x{X:0>8} which is a coprocessor data processing instr", .{opcode});
log.err("TODO: handle 0x{X:0>8} which is a coprocessor data processing instr", .{opcode});
} }
}.inner; }.inner;
} }

View File

@ -1,6 +1,9 @@
const std = @import("std");
const sext = @import("zba-util").sext; const sext = @import("zba-util").sext;
const rotr = @import("zba-util").rotr; const rotr = @import("zba-util").rotr;
const log = std.log.scoped(.half_and_signed_data_transfer);
pub fn halfAndSignedDataTransfer(comptime InstrFn: type, comptime P: bool, comptime U: bool, comptime I: bool, comptime W: bool, comptime L: bool) InstrFn { pub fn halfAndSignedDataTransfer(comptime InstrFn: type, comptime P: bool, comptime U: bool, comptime I: bool, comptime W: bool, comptime L: bool) InstrFn {
const Arm32 = @typeInfo(@typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?).Pointer.child; const Arm32 = @typeInfo(@typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?).Pointer.child;
@ -66,9 +69,9 @@ pub fn halfAndSignedDataTransfer(comptime InstrFn: type, comptime P: bool, compt
// FIXME: I shouldn't have to use @as(u16, ...) here // FIXME: I shouldn't have to use @as(u16, ...) here
cpu.write(u16, address, @as(u16, @truncate(cpu.r[rd]))); cpu.write(u16, address, @as(u16, @truncate(cpu.r[rd])));
}, },
0b10 => { 0b10 => blk: {
// LDRD // LDRD
if (Arm32.arch != .v5te) cpu.panic("LDRD: unsupported on arm{s}", .{@tagName(Arm32.arch)}); if (Arm32.arch == .v4t) break :blk;
if (rd & 0 != 0) cpu.panic("LDRD: UNDEFINED behaviour when Rd is not even", .{}); if (rd & 0 != 0) cpu.panic("LDRD: UNDEFINED behaviour when Rd is not even", .{});
if (rd == 0xE) cpu.panic("LDRD: UNPREDICTABLE behaviour when rd == 14", .{}); if (rd == 0xE) cpu.panic("LDRD: UNPREDICTABLE behaviour when rd == 14", .{});
if (address & 0x7 != 0b000) cpu.panic("LDRD: UNPREDICTABLE when address (0x{X:0>8} is not double (64-bit) aligned", .{address}); if (address & 0x7 != 0b000) cpu.panic("LDRD: UNPREDICTABLE when address (0x{X:0>8} is not double (64-bit) aligned", .{address});

View File

@ -30,6 +30,7 @@ pub fn control(comptime InstrFn: type, comptime I: bool, comptime op: u6) InstrF
}, },
0b01_0001 => cpu.panic("TODO: implement v5TE BX", .{}), 0b01_0001 => cpu.panic("TODO: implement v5TE BX", .{}),
0b11_0001 => { // CLZ 0b11_0001 => { // CLZ
if (Arm32.arch == .v4t) return cpu.undefinedInstructionTrap();
const rd = opcode >> 12 & 0xF; const rd = opcode >> 12 & 0xF;
const rm = opcode & 0xF; const rm = opcode & 0xF;
@ -50,6 +51,7 @@ pub fn control(comptime InstrFn: type, comptime I: bool, comptime op: u6) InstrF
cpu.pipe.reload(cpu); cpu.pipe.reload(cpu);
}, },
0b00_0101, 0b01_0101, 0b10_0101, 0b11_0101 => { // QADD / QDADD / QSUB / QDSUB 0b00_0101, 0b01_0101, 0b10_0101, 0b11_0101 => { // QADD / QDADD / QSUB / QDSUB
if (Arm32.arch == .v4t) return cpu.undefinedInstructionTrap();
const U = op >> 4 & 1 == 1; const U = op >> 4 & 1 == 1;
const D = op >> 5 & 1 == 1; const D = op >> 5 & 1 == 1;
@ -84,6 +86,7 @@ pub fn control(comptime InstrFn: type, comptime I: bool, comptime op: u6) InstrF
}, },
0b01_0111 => cpu.panic("TODO: handle BKPT", .{}), 0b01_0111 => cpu.panic("TODO: handle BKPT", .{}),
0b00_1000, 0b00_1010, 0b00_1100, 0b00_1110 => { // SMLA<x><y> 0b00_1000, 0b00_1010, 0b00_1100, 0b00_1110 => { // SMLA<x><y>
if (Arm32.arch == .v4t) return; // no-op
const X = op >> 1 & 1; const X = op >> 1 & 1;
const Y = op >> 2 & 1; const Y = op >> 2 & 1;

View File

@ -20,6 +20,8 @@ pub const arm = struct {
/// Arithmetic Instruction Extension Space /// Arithmetic Instruction Extension Space
const multiplyExt = @import("cpu/arm/multiply.zig").multiply; const multiplyExt = @import("cpu/arm/multiply.zig").multiply;
const cop = @import("cpu/arm/coprocessor.zig");
/// 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?
@ -87,8 +89,29 @@ pub const arm = struct {
const L = i >> 8 & 1 == 1; const L = i >> 8 & 1 == 1;
break :blk branch(InstrFn, L); break :blk branch(InstrFn, L);
}, },
0b10 => und, // COP Data Transfer 0b10 => blk: {
0b11 => if (i >> 8 & 1 == 1) swi(InstrFn) else und, // COP Data Operation + Register Transfer 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),
};
},
}, },
}; };
} }