diff --git a/src/arm/cpu/arm/block_data_transfer.zig b/src/arm/cpu/arm/block_data_transfer.zig index acd38f5..9334a3a 100644 --- a/src/arm/cpu/arm/block_data_transfer.zig +++ b/src/arm/cpu/arm/block_data_transfer.zig @@ -3,6 +3,8 @@ pub fn blockDataTransfer(comptime InstrFn: type, comptime P: bool, comptime U: b return struct { fn inner(cpu: *Arm32, opcode: u32) void { + if (opcode == @byteSwap(0x030020E9)) @breakpoint(); + const rn: u4 = @truncate(opcode >> 16 & 0xF); const rlist: u16 = @intCast(opcode & 0xFFFF); @@ -20,8 +22,6 @@ pub fn blockDataTransfer(comptime InstrFn: type, comptime P: bool, comptime U: b break :blk base_addr - (4 * reg_count) + if (!P) 4 else 0; }; - // FIXME : why 4 * reg_count? - const new_base_addr: u32 = if (U) blk: { break :blk base_addr + 4 * reg_count; } else blk: { @@ -77,23 +77,47 @@ pub fn blockDataTransfer(comptime InstrFn: type, comptime P: bool, comptime U: b } } + { + const cond = @as(u2, @intFromBool(P)) << 1 | @intFromBool(U); + + const end_address: u32 = switch (cond) { + 0b00 => base_addr, // Decrement After + 0b10 => base_addr - 4, // Increment Before + 0b01 => base_addr + (4 * reg_count) - 4, // Increment After + 0b11 => base_addr + (4 * reg_count), // Increment Before + }; + + const actual_addr = address - 4; + + if (actual_addr != end_address) { + cpu.panic( + \\P = {}, U = {}, S = {}, W = {}, L = {} + \\base_addr = 0x{X:0>8} + \\start_addr = 0x{X:0>8} + \\expected_end_address = 0x{X:0>8} actual_end_address = 0x{X:0>8} + \\rlist = 0b{b:0>16}, first_in_list = r{} + , .{ P, U, S, W, L, base_addr, start_addr, end_address, actual_addr, rlist, first_in_list }); + } + } + // What happens when W is set and Rn is in the rlist? (STM) // // Armv4: Store OLD Base if Rb is FIRST entry in Rlist, otherwise store NEW base // Armv5: Always store OLD Base if (W and !L) { - const rn_in_rlist = rlist >> rn & 1 == 1; + if (rlist >> rn & 1 == 0) { // rn is not in rlist + cpu.r[rn] = new_base_addr; + return; + } - if (rn_in_rlist) { - const mask = @as(u16, 1) << rn; - const is_first = @popCount(rlist & (mask - 1)) == 0; + const mask = @as(u16, 1) << rn; + const is_first = @popCount(rlist & (mask - 1)) == 0; - cpu.r[rn] = switch (Arm32.arch) { - .v4t => if (is_first) base_addr else new_base_addr, - .v5te => base_addr, - }; - } else cpu.r[rn] = new_base_addr; + cpu.r[rn] = switch (Arm32.arch) { + .v4t => if (is_first) base_addr else new_base_addr, + .v5te => base_addr, + }; } // What happens when W is set and Rn is in the rlist? (LDM) @@ -102,7 +126,7 @@ pub fn blockDataTransfer(comptime InstrFn: type, comptime P: bool, comptime U: b // ARMv5: writeback if Rn is "the ONLY register" or NOT the LAST register if (W and L) { - if (rlist >> rn & 1 == 0) { + if (rlist >> rn & 1 == 0) { // rn is not in rlist cpu.r[rn] = new_base_addr; return; }