From aad3bdc9ea44792906a6cff9e28c15cd4e143389 Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Wed, 27 Dec 2023 00:11:42 -0600 Subject: [PATCH] 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 --- src/arm.zig | 23 ++++++++++++++++ src/arm/cpu/arm/coprocessor.zig | 19 ++++++------- src/arm/cpu/arm/half_signed_data_transfer.zig | 7 +++-- src/arm/cpu/arm/psr_transfer.zig | 3 +++ src/arm/v4t.zig | 27 +++++++++++++++++-- 5 files changed, 66 insertions(+), 13 deletions(-) diff --git a/src/arm.zig b/src/arm.zig index cd08562..8ad1386 100644 --- a/src/arm.zig +++ b/src/arm.zig @@ -409,6 +409,29 @@ pub fn Arm32(comptime isa: Architecture) type { 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 { return switch (isa) { .v4t => .{ .v4t = self }, diff --git a/src/arm/cpu/arm/coprocessor.zig b/src/arm/cpu/arm/coprocessor.zig index 52d5a74..56b935d 100644 --- a/src/arm/cpu/arm/coprocessor.zig +++ b/src/arm/cpu/arm/coprocessor.zig @@ -51,14 +51,13 @@ pub fn dataTransfer( // TODO: Increment address + 4 (and perform op) until coprocessor says stop 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 { - 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 { - _ = cpu; const cp_num = opcode >> 8 & 0xF; const rd = opcode >> 12 & 0xF; const rn = opcode >> 16 & 0xF; @@ -71,10 +70,10 @@ pub fn dataTransfer( if (L) { // 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 { // 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; @@ -90,7 +89,11 @@ pub fn registerTransfer(comptime InstrFn: type, comptime opcode1: u3, comptime L const cp_num = opcode >> 8 & 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) { // MRC @@ -148,9 +151,7 @@ pub fn dataProcessing(comptime InstrFn: type, comptime opcode1: u4, comptime opc 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}); + cpu.panic("TODO: handle 0x{X:0>8} which is a coprocessor data processing instr", .{opcode}); } }.inner; } diff --git a/src/arm/cpu/arm/half_signed_data_transfer.zig b/src/arm/cpu/arm/half_signed_data_transfer.zig index 026ed4e..d71e60b 100644 --- a/src/arm/cpu/arm/half_signed_data_transfer.zig +++ b/src/arm/cpu/arm/half_signed_data_transfer.zig @@ -1,6 +1,9 @@ +const std = @import("std"); const sext = @import("zba-util").sext; 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 { 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 cpu.write(u16, address, @as(u16, @truncate(cpu.r[rd]))); }, - 0b10 => { + 0b10 => blk: { // 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 == 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}); diff --git a/src/arm/cpu/arm/psr_transfer.zig b/src/arm/cpu/arm/psr_transfer.zig index 3280987..ef4219b 100644 --- a/src/arm/cpu/arm/psr_transfer.zig +++ b/src/arm/cpu/arm/psr_transfer.zig @@ -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", .{}), 0b11_0001 => { // CLZ + if (Arm32.arch == .v4t) return cpu.undefinedInstructionTrap(); const rd = opcode >> 12 & 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); }, 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 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", .{}), 0b00_1000, 0b00_1010, 0b00_1100, 0b00_1110 => { // SMLA + if (Arm32.arch == .v4t) return; // no-op const X = op >> 1 & 1; const Y = op >> 2 & 1; diff --git a/src/arm/v4t.zig b/src/arm/v4t.zig index 3c6d1b2..8db0c58 100644 --- a/src/arm/v4t.zig +++ b/src/arm/v4t.zig @@ -20,6 +20,8 @@ pub const arm = struct { /// Arithmetic Instruction Extension Space const multiplyExt = @import("cpu/arm/multiply.zig").multiply; + const cop = @import("cpu/arm/coprocessor.zig"); + /// Determine index into ARM InstrFn LUT pub fn idx(opcode: u32) u12 { // FIXME: omit these? @@ -87,8 +89,29 @@ pub const arm = struct { 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 + 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), + }; + }, }, }; }