diff --git a/src/arm/cpu/arm/block_data_transfer.zig b/src/arm/cpu/arm/block_data_transfer.zig index 574fd3d..acd38f5 100644 --- a/src/arm/cpu/arm/block_data_transfer.zig +++ b/src/arm/cpu/arm/block_data_transfer.zig @@ -12,18 +12,20 @@ pub fn blockDataTransfer(comptime InstrFn: type, comptime P: bool, comptime U: b // U determines whether the LDM/STM transfer is made upwards (U == 1) // or downwards (U == 0). + const base_addr = cpu.r[rn]; + const start_addr: u32 = if (U) blk: { - break :blk cpu.r[rn] + if (P) 4 else 0; + break :blk base_addr + if (P) 4 else 0; } else blk: { - break :blk cpu.r[rn] - (4 * reg_count) + if (!P) 4 else 0; + 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 cpu.r[rn] + 4 * reg_count; + break :blk base_addr + 4 * reg_count; } else blk: { - break :blk cpu.r[rn] - 4 * reg_count; + break :blk base_addr - 4 * reg_count; }; var address = start_addr; @@ -36,9 +38,9 @@ pub fn blockDataTransfer(comptime InstrFn: type, comptime P: bool, comptime U: b if (rlist == 0) { if (Arm32.arch == .v4t) { const undefined_addr: u32 = if (U) blk: { - break :blk cpu.r[rn] + if (P) 4 else 0; + break :blk base_addr + if (P) 4 else 0; } else blk: { - break :blk cpu.r[rn] - (0x40 - if (!P) 4 else 0); + break :blk base_addr - (0x40 - if (!P) 4 else 0); }; if (L) { @@ -49,20 +51,14 @@ pub fn blockDataTransfer(comptime InstrFn: type, comptime P: bool, comptime U: b } } - cpu.r[rn] = if (U) cpu.r[rn] + 0x40 else cpu.r[rn] - 0x40; + cpu.r[rn] = if (U) base_addr + 0x40 else base_addr - 0x40; return; } - // 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 - // FIXME: This absolutely needs revisiting :skull: - const r15_present = rlist >> 15 & 1 == 1; - var write_to_base = true; + // var first = true; for (first_in_list..16) |idx| { const i: u4 = @intCast(idx); @@ -70,13 +66,36 @@ pub fn blockDataTransfer(comptime InstrFn: type, comptime P: bool, comptime U: b transfer(cpu, r15_present, i, address); address += 4; - if (W and !L and write_to_base) { - cpu.r[rn] = new_base_addr; - write_to_base = false; - } + // if (W and !L and first) { + // cpu.r[rn] = switch (Arm32.arch) { + // .v4t => new_base_addr, + // .v5te => start_addr, + // }; + + // first = false; + // } } } + // 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 (rn_in_rlist) { + 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; + } + // What happens when W is set and Rn is in the rlist? (LDM) // // ARMv4: No writeback