From 5bb5bdf3896b20a80b82ff08c6ad2b7fe645dadc Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Wed, 28 Sep 2022 12:46:32 -0300 Subject: [PATCH] chore: refactor ARM/THUMB data processing instructions --- src/core/cpu/arm/data_processing.zig | 68 +++--------- src/core/cpu/thumb/alu.zig | 57 +++++----- src/core/cpu/thumb/data_processing.zig | 137 ++++++++++++++++--------- 3 files changed, 132 insertions(+), 130 deletions(-) diff --git a/src/core/cpu/arm/data_processing.zig b/src/core/cpu/arm/data_processing.zig index 5540d28..604921c 100644 --- a/src/core/cpu/arm/data_processing.zig +++ b/src/core/cpu/arm/data_processing.zig @@ -24,7 +24,7 @@ pub fn dataProcessing(comptime I: bool, comptime S: bool, comptime kind: u4) Ins if (!I and opcode >> 4 & 1 == 1) cpu.r[15] -= 4; var result: u32 = undefined; - var didOverflow: bool = undefined; + var overflow: bool = undefined; // Perform Data Processing Logic switch (kind) { @@ -32,8 +32,8 @@ pub fn dataProcessing(comptime I: bool, comptime S: bool, comptime kind: u4) Ins 0x1 => result = op1 ^ op2, // EOR 0x2 => result = op1 -% op2, // SUB 0x3 => result = op2 -% op1, // RSB - 0x4 => result = newAdd(&didOverflow, op1, op2), // ADD - 0x5 => result = newAdc(&didOverflow, op1, op2, old_carry), // ADC + 0x4 => result = add(&overflow, op1, op2), // ADD + 0x5 => result = adc(&overflow, op1, op2, old_carry), // ADC 0x6 => result = sbc(op1, op2, old_carry), // SBC 0x7 => result = sbc(op2, op1, old_carry), // RSC 0x8 => { @@ -62,7 +62,7 @@ pub fn dataProcessing(comptime I: bool, comptime S: bool, comptime kind: u4) Ins if (rd == 0xF) return undefinedTestBehaviour(cpu); - didOverflow = @addWithOverflow(u32, op1, op2, &result); + overflow = @addWithOverflow(u32, op1, op2, &result); }, 0xC => result = op1 | op2, // ORR 0xD => result = op2, // MOV @@ -111,7 +111,7 @@ pub fn dataProcessing(comptime I: bool, comptime S: bool, comptime kind: u4) Ins // ADD, ADC Flags cpu.cpsr.n.write(result >> 31 & 1 == 1); cpu.cpsr.z.write(result == 0); - cpu.cpsr.c.write(didOverflow); + cpu.cpsr.c.write(overflow); cpu.cpsr.v.write(((op1 ^ result) & (op2 ^ result)) >> 31 & 1 == 1); }, 0x6, 0x7 => if (S and rd != 0xF) { @@ -142,7 +142,7 @@ pub fn dataProcessing(comptime I: bool, comptime S: bool, comptime kind: u4) Ins cpu.cpsr.v.write(((op1 ^ result) & (~op2 ^ result)) >> 31 & 1 == 1); } else if (kind == 0xB) { // CMN specific - cpu.cpsr.c.write(didOverflow); + cpu.cpsr.c.write(overflow); cpu.cpsr.v.write(((op1 ^ result) & (op2 ^ result)) >> 31 & 1 == 1); } else { // TST, TEQ specific @@ -163,65 +163,21 @@ pub fn sbc(left: u32, right: u32, old_carry: u1) u32 { return ret; } -pub fn sub(comptime S: bool, cpu: *Arm7tdmi, left: u32, right: u32) u32 { - const result = left -% right; - - if (S) { - cpu.cpsr.n.write(result >> 31 & 1 == 1); - cpu.cpsr.z.write(result == 0); - cpu.cpsr.c.write(right <= left); - cpu.cpsr.v.write(((left ^ result) & (~right ^ result)) >> 31 & 1 == 1); - } - - return result; -} - -fn newAdd(didOverflow: *bool, left: u32, right: u32) u32 { +pub fn add(overflow: *bool, left: u32, right: u32) u32 { var ret: u32 = undefined; - didOverflow.* = @addWithOverflow(u32, left, right, &ret); + overflow.* = @addWithOverflow(u32, left, right, &ret); return ret; } -pub fn add(comptime S: bool, cpu: *Arm7tdmi, left: u32, right: u32) u32 { - var result: u32 = undefined; - const didOverflow = @addWithOverflow(u32, left, right, &result); - - if (S) { - cpu.cpsr.n.write(result >> 31 & 1 == 1); - cpu.cpsr.z.write(result == 0); - cpu.cpsr.c.write(didOverflow); - cpu.cpsr.v.write(((left ^ result) & (right ^ result)) >> 31 & 1 == 1); - } - - return result; -} - -pub fn newAdc(didOverflow: *bool, left: u32, right: u32, old_carry: u1) u32 { +pub fn adc(overflow: *bool, left: u32, right: u32, old_carry: u1) u32 { var ret: u32 = undefined; - const did = @addWithOverflow(u32, left, right, &ret); - const overflow = @addWithOverflow(u32, ret, old_carry, &ret); + const first = @addWithOverflow(u32, left, right, &ret); + const second = @addWithOverflow(u32, ret, old_carry, &ret); - didOverflow.* = did or overflow; + overflow.* = first or second; return ret; } -pub fn cmp(cpu: *Arm7tdmi, left: u32, right: u32) void { - const result = left -% right; - - cpu.cpsr.n.write(result >> 31 & 1 == 1); - cpu.cpsr.z.write(result == 0); - cpu.cpsr.c.write(right <= left); - cpu.cpsr.v.write(((left ^ result) & (~right ^ result)) >> 31 & 1 == 1); -} - -pub fn setLogicOpFlags(comptime S: bool, cpu: *Arm7tdmi, result: u32) void { - if (S) { - cpu.cpsr.n.write(result >> 31 & 1 == 1); - cpu.cpsr.z.write(result == 0); - // C set by Barrel Shifter, V is unaffected - } -} - fn undefinedTestBehaviour(cpu: *Arm7tdmi) void { @setCold(true); cpu.setCpsrNoFlush(cpu.spsr.raw); diff --git a/src/core/cpu/thumb/alu.zig b/src/core/cpu/thumb/alu.zig index d16ae5d..a05b26b 100644 --- a/src/core/cpu/thumb/alu.zig +++ b/src/core/cpu/thumb/alu.zig @@ -2,7 +2,7 @@ const Bus = @import("../../Bus.zig"); const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi; const InstrFn = @import("../../cpu.zig").thumb.InstrFn; -const adc = @import("../arm/data_processing.zig").newAdc; +const adc = @import("../arm/data_processing.zig").adc; const sbc = @import("../arm/data_processing.zig").sbc; const lsl = @import("../barrel_shifter.zig").logicalLeft; @@ -17,25 +17,28 @@ pub fn fmt4(comptime op: u4) InstrFn { const rd = opcode & 0x7; const carry = @boolToInt(cpu.cpsr.c.read()); + const op1 = cpu.r[rd]; + const op2 = cpu.r[rs]; + var result: u32 = undefined; - var didOverflow: bool = undefined; + var overflow: bool = undefined; switch (op) { - 0x0 => result = cpu.r[rd] & cpu.r[rs], // AND - 0x1 => result = cpu.r[rd] ^ cpu.r[rs], // EOR - 0x2 => result = lsl(true, &cpu.cpsr, cpu.r[rd], @truncate(u8, cpu.r[rs])), // LSL - 0x3 => result = lsr(true, &cpu.cpsr, cpu.r[rd], @truncate(u8, cpu.r[rs])), // LSR - 0x4 => result = asr(true, &cpu.cpsr, cpu.r[rd], @truncate(u8, cpu.r[rs])), // ASR - 0x5 => result = adc(&didOverflow, cpu.r[rd], cpu.r[rs], carry), // ADC - 0x6 => result = sbc(cpu.r[rd], cpu.r[rs], carry), // SBC - 0x7 => result = ror(true, &cpu.cpsr, cpu.r[rd], @truncate(u8, cpu.r[rs])), // ROR - 0x8 => result = cpu.r[rd] & cpu.r[rs], // TST - 0x9 => result = 0 -% cpu.r[rs], // NEG - 0xA => result = cpu.r[rd] -% cpu.r[rs], // CMP - 0xB => didOverflow = @addWithOverflow(u32, cpu.r[rd], cpu.r[rs], &result), // CMN - 0xC => result = cpu.r[rd] | cpu.r[rs], // ORR - 0xD => result = @truncate(u32, @as(u64, cpu.r[rs]) * @as(u64, cpu.r[rd])), - 0xE => result = cpu.r[rd] & ~cpu.r[rs], - 0xF => result = ~cpu.r[rs], + 0x0 => result = op1 & op2, // AND + 0x1 => result = op1 ^ op2, // EOR + 0x2 => result = lsl(true, &cpu.cpsr, op1, @truncate(u8, op2)), // LSL + 0x3 => result = lsr(true, &cpu.cpsr, op1, @truncate(u8, op2)), // LSR + 0x4 => result = asr(true, &cpu.cpsr, op1, @truncate(u8, op2)), // ASR + 0x5 => result = adc(&overflow, op1, op2, carry), // ADC + 0x6 => result = sbc(op1, op2, carry), // SBC + 0x7 => result = ror(true, &cpu.cpsr, op1, @truncate(u8, op2)), // ROR + 0x8 => result = op1 & op2, // TST + 0x9 => result = 0 -% op2, // NEG + 0xA => result = op1 -% op2, // CMP + 0xB => overflow = @addWithOverflow(u32, op1, op2, &result), // CMN + 0xC => result = op1 | op2, // ORR + 0xD => result = @truncate(u32, @as(u64, op2) * @as(u64, op1)), + 0xE => result = op1 & ~op2, + 0xF => result = ~op2, } // Write to Destination Register @@ -60,16 +63,16 @@ pub fn fmt4(comptime op: u4) InstrFn { if (op == 0xA) { // CMP specific - cpu.cpsr.c.write(cpu.r[rs] <= cpu.r[rd]); - cpu.cpsr.v.write(((cpu.r[rd] ^ result) & (~cpu.r[rs] ^ result)) >> 31 & 1 == 1); + cpu.cpsr.c.write(op2 <= op1); + cpu.cpsr.v.write(((op1 ^ result) & (~op2 ^ result)) >> 31 & 1 == 1); } }, 0x5, 0xB => { // ADC, CMN cpu.cpsr.n.write(result >> 31 & 1 == 1); cpu.cpsr.z.write(result == 0); - cpu.cpsr.c.write(didOverflow); - cpu.cpsr.v.write(((cpu.r[rd] ^ result) & (cpu.r[rs] ^ result)) >> 31 & 1 == 1); + cpu.cpsr.c.write(overflow); + cpu.cpsr.v.write(((op1 ^ result) & (op2 ^ result)) >> 31 & 1 == 1); // FIXME: Pretty sure CMN Is the same }, @@ -78,16 +81,16 @@ pub fn fmt4(comptime op: u4) InstrFn { cpu.cpsr.n.write(result >> 31 & 1 == 1); cpu.cpsr.z.write(result == 0); - const subtrahend = @as(u64, cpu.r[rs]) -% carry +% 1; - cpu.cpsr.c.write(subtrahend <= cpu.r[rd]); - cpu.cpsr.v.write(((cpu.r[rd] ^ result) & (~cpu.r[rs] ^ result)) >> 31 & 1 == 1); + const subtrahend = @as(u64, op2) -% carry +% 1; + cpu.cpsr.c.write(subtrahend <= op1); + cpu.cpsr.v.write(((op1 ^ result) & (~op2 ^ result)) >> 31 & 1 == 1); }, 0x9 => { // NEG cpu.cpsr.n.write(result >> 31 & 1 == 1); cpu.cpsr.z.write(result == 0); - cpu.cpsr.c.write(cpu.r[rs] <= 0); - cpu.cpsr.v.write(((0 ^ result) & (~cpu.r[rs] ^ result)) >> 31 & 1 == 1); + cpu.cpsr.c.write(op2 <= 0); + cpu.cpsr.v.write(((0 ^ result) & (~op2 ^ result)) >> 31 & 1 == 1); }, 0xD => { // Multiplication diff --git a/src/core/cpu/thumb/data_processing.zig b/src/core/cpu/thumb/data_processing.zig index ebc897a..e0e6c50 100644 --- a/src/core/cpu/thumb/data_processing.zig +++ b/src/core/cpu/thumb/data_processing.zig @@ -3,12 +3,12 @@ const std = @import("std"); const Bus = @import("../../Bus.zig"); const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi; const InstrFn = @import("../../cpu.zig").thumb.InstrFn; -const shifter = @import("../barrel_shifter.zig"); const add = @import("../arm/data_processing.zig").add; -const sub = @import("../arm/data_processing.zig").sub; -const cmp = @import("../arm/data_processing.zig").cmp; -const setLogicOpFlags = @import("../arm/data_processing.zig").setLogicOpFlags; + +const lsl = @import("../barrel_shifter.zig").logicalLeft; +const lsr = @import("../barrel_shifter.zig").logicalRight; +const asr = @import("../barrel_shifter.zig").arithmeticRight; pub fn fmt1(comptime op: u2, comptime offset: u5) InstrFn { return struct { @@ -22,7 +22,7 @@ pub fn fmt1(comptime op: u2, comptime offset: u5) InstrFn { if (offset == 0) { break :blk cpu.r[rs]; } else { - break :blk shifter.logicalLeft(true, &cpu.cpsr, cpu.r[rs], offset); + break :blk lsl(true, &cpu.cpsr, cpu.r[rs], offset); } }, 0b01 => blk: { @@ -31,7 +31,7 @@ pub fn fmt1(comptime op: u2, comptime offset: u5) InstrFn { cpu.cpsr.c.write(cpu.r[rs] >> 31 & 1 == 1); break :blk @as(u32, 0); } else { - break :blk shifter.logicalRight(true, &cpu.cpsr, cpu.r[rs], offset); + break :blk lsr(true, &cpu.cpsr, cpu.r[rs], offset); } }, 0b10 => blk: { @@ -40,7 +40,7 @@ pub fn fmt1(comptime op: u2, comptime offset: u5) InstrFn { cpu.cpsr.c.write(cpu.r[rs] >> 31 & 1 == 1); break :blk @bitCast(u32, @bitCast(i32, cpu.r[rs]) >> 31); } else { - break :blk shifter.arithmeticRight(true, &cpu.cpsr, cpu.r[rs], offset); + break :blk asr(true, &cpu.cpsr, cpu.r[rs], offset); } }, else => cpu.panic("[CPU/THUMB.1] 0b{b:0>2} is not a valid op", .{op}), @@ -48,7 +48,10 @@ pub fn fmt1(comptime op: u2, comptime offset: u5) InstrFn { // Equivalent to an ARM MOVS cpu.r[rd] = result; - setLogicOpFlags(true, cpu, result); + + // Write Flags + cpu.cpsr.n.write(result >> 31 & 1 == 1); + cpu.cpsr.z.write(result == 0); } }.inner; } @@ -59,35 +62,49 @@ pub fn fmt5(comptime op: u2, comptime h1: u1, comptime h2: u1) InstrFn { const rs = @as(u4, h2) << 3 | (opcode >> 3 & 0x7); const rd = @as(u4, h1) << 3 | (opcode & 0x7); - const rs_value = if (rs == 0xF) cpu.r[rs] & ~@as(u32, 1) else cpu.r[rs]; - const rd_value = if (rd == 0xF) cpu.r[rd] & ~@as(u32, 1) else cpu.r[rd]; + const op1 = cpu.r[rd]; + const op2 = cpu.r[rs]; + var result: u32 = undefined; + var overflow: bool = undefined; switch (op) { - 0b00 => { - // ADD - const sum = add(false, cpu, rd_value, rs_value); - cpu.r[rd] = if (rd == 0xF) sum & ~@as(u32, 1) else sum; - }, - 0b01 => cmp(cpu, rd_value, rs_value), // CMP - 0b10 => { - // MOV - cpu.r[rd] = if (rd == 0xF) rs_value & ~@as(u32, 1) else rs_value; - }, + 0b00 => result = add(&overflow, op1, op2), // ADD + 0b01 => result = op1 -% op2, // CMP + 0b10 => result = op2, // MOV + 0b11 => {}, + } + + // Write to Destination Register + switch (op) { + 0b01 => {}, // Test Instruction 0b11 => { // BX - const thumb = rs_value & 1 == 1; - cpu.r[15] = rs_value & ~@as(u32, 1); + const is_thumb = op2 & 1 == 1; + cpu.r[15] = op2 & ~@as(u32, 1); - cpu.cpsr.t.write(thumb); - if (thumb) cpu.pipe.reload(u16, cpu) else cpu.pipe.reload(u32, cpu); - - // TODO: We shouldn't need to worry about the if statement - // below, because in BX, rd SBZ (and H1 is guaranteed to be 0) - return; + cpu.cpsr.t.write(is_thumb); + if (is_thumb) cpu.pipe.reload(u16, cpu) else cpu.pipe.reload(u32, cpu); + }, + else => { + cpu.r[rd] = result; + if (rd == 0xF) { + cpu.r[15] &= ~@as(u32, 1); + cpu.pipe.reload(u16, cpu); + } }, } - if (rd == 0xF) cpu.pipe.reload(u16, cpu); + // Write Flags + switch (op) { + 0b01 => { + // CMP + cpu.cpsr.n.write(result >> 31 & 1 == 1); + cpu.cpsr.z.write(result == 0); + cpu.cpsr.c.write(op2 <= op1); + cpu.cpsr.v.write(((op1 ^ result) & (~op2 ^ result)) >> 31 & 1 == 1); + }, + 0b00, 0b10, 0b11 => {}, // MOV and Branch Instruction + } } }.inner; } @@ -97,21 +114,28 @@ pub fn fmt2(comptime I: bool, is_sub: bool, rn: u3) InstrFn { fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void { const rs = opcode >> 3 & 0x7; const rd = @truncate(u3, opcode); + const op1 = cpu.r[rs]; + const op2: u32 = if (I) rn else cpu.r[rn]; if (is_sub) { // SUB - cpu.r[rd] = if (I) blk: { - break :blk sub(true, cpu, cpu.r[rs], rn); - } else blk: { - break :blk sub(true, cpu, cpu.r[rs], cpu.r[rn]); - }; + const result = op1 -% op2; + cpu.r[rd] = result; + + cpu.cpsr.n.write(result >> 31 & 1 == 1); + cpu.cpsr.z.write(result == 0); + cpu.cpsr.c.write(op2 <= op1); + cpu.cpsr.v.write(((op1 ^ result) & (~op2 ^ result)) >> 31 & 1 == 1); } else { // ADD - cpu.r[rd] = if (I) blk: { - break :blk add(true, cpu, cpu.r[rs], rn); - } else blk: { - break :blk add(true, cpu, cpu.r[rs], cpu.r[rn]); - }; + var overflow: bool = undefined; + const result = add(&overflow, op1, op2); + cpu.r[rd] = result; + + cpu.cpsr.n.write(result >> 31 & 1 == 1); + cpu.cpsr.z.write(result == 0); + cpu.cpsr.c.write(overflow); + cpu.cpsr.v.write(((op1 ^ result) & (op2 ^ result)) >> 31 & 1 == 1); } } }.inner; @@ -120,17 +144,36 @@ pub fn fmt2(comptime I: bool, is_sub: bool, rn: u3) InstrFn { pub fn fmt3(comptime op: u2, comptime rd: u3) InstrFn { return struct { fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void { - const offset = @truncate(u8, opcode); + const op1 = cpu.r[rd]; + const op2: u32 = opcode & 0xFF; // Offset + + var overflow: bool = undefined; + const result: u32 = switch (op) { + 0b00 => op2, // MOV + 0b01 => op1 -% op2, // CMP + 0b10 => add(&overflow, op1, op2), // ADD + 0b11 => op1 -% op2, // SUB + }; + + // Write to Register + if (op != 0b01) cpu.r[rd] = result; + + // Write Flags + cpu.cpsr.n.write(result >> 31 & 1 == 1); + cpu.cpsr.z.write(result == 0); switch (op) { - 0b00 => { - // MOV - cpu.r[rd] = offset; - setLogicOpFlags(true, cpu, offset); + 0b00 => {}, // MOV | C set by Barrel Shifter, V is unaffected + 0b01, 0b11 => { + // SUB, CMP + cpu.cpsr.c.write(op2 <= op1); + cpu.cpsr.v.write(((op1 ^ result) & (~op2 ^ result)) >> 31 & 1 == 1); + }, + 0b10 => { + // ADD + cpu.cpsr.c.write(overflow); + cpu.cpsr.v.write(((op1 ^ result) & (op2 ^ result)) >> 31 & 1 == 1); }, - 0b01 => cmp(cpu, cpu.r[rd], offset), // CMP - 0b10 => cpu.r[rd] = add(true, cpu, cpu.r[rd], offset), // ADD - 0b11 => cpu.r[rd] = sub(true, cpu, cpu.r[rd], offset), // SUB } } }.inner;