Skip to content

Commit bdc6eb4

Browse files
authored
Merge pull request #331 from w1ne/fix/wide-extend-and-add
fix(cpu): decode wide extend-and-add (UXTAH/UXTAB/SXTAH/SXTAB)
2 parents c8d8d03 + d13f56f commit bdc6eb4

3 files changed

Lines changed: 53 additions & 16 deletions

File tree

crates/core/src/cpu/cortex_m.rs

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1494,14 +1494,27 @@ impl CortexM {
14941494
let val = self.read_reg(rm) as u16 as i16 as i32 as u32;
14951495
self.write_reg(rd, val);
14961496
}
1497-
Instruction::ExtendW { rd, rm, rotate, op } => {
1497+
Instruction::ExtendW {
1498+
rd,
1499+
rn,
1500+
rm,
1501+
rotate,
1502+
op,
1503+
} => {
14981504
// ROR Rm by `rotate` (0/8/16/24), then extract+extend.
14991505
let v = self.read_reg(rm).rotate_right(rotate as u32);
1500-
let out = match op {
1501-
0b000 => v as u16 as i16 as i32 as u32, // SXTH.W
1502-
0b001 => v & 0xFFFF, // UXTH.W
1503-
0b100 => v as u8 as i8 as i32 as u32, // SXTB.W
1504-
_ => v & 0xFF, // UXTB.W (0b101)
1506+
let ext = match op {
1507+
0b000 => v as u16 as i16 as i32 as u32, // S*XTH
1508+
0b001 => v & 0xFFFF, // U*XTH
1509+
0b100 => v as u8 as i8 as i32 as u32, // S*XTB
1510+
_ => v & 0xFF, // U*XTB (0b101)
1511+
};
1512+
// Extend-and-add variants (Rn != 0xF) add Rn; the plain
1513+
// extends encode Rn = 0xF.
1514+
let out = if rn == 0xF {
1515+
ext
1516+
} else {
1517+
self.read_reg(rn).wrapping_add(ext)
15051518
};
15061519
self.write_reg(rd, out);
15071520
}
@@ -3063,6 +3076,19 @@ mod tests {
30633076
// ROR #8 of 0x00850000 = 0x00008500; & 0xFFFF = 0x8500.
30643077
assert_eq!(cpu.r0, 0x0000_8500, "UXTH.W ROR #8 must rotate then extend");
30653078
}
3079+
// UXTAH R0, R1, R2 = FA11 F082 — R0 = R1 + uxth(R2) (extend-and-add).
3080+
// This is the `4 + path_len` form (uxtah r6,r3,r0) that the plain-extend
3081+
// decode missed, leaving the result register stale.
3082+
{
3083+
let mut cpu = CortexM::new();
3084+
let mut bus = MockBus::new();
3085+
cpu.pc = 0x2000;
3086+
cpu.r0 = 0xDEAD_BEEF; // stale value that must be overwritten
3087+
cpu.r1 = 0x0000_0004;
3088+
cpu.r2 = 0x1234_0002;
3089+
run_test_instr(&mut cpu, &mut bus, 0xFA11F082, true);
3090+
assert_eq!(cpu.r0, 0x0000_0006, "UXTAH must add Rn to the extended Rm");
3091+
}
30663092
}
30673093

30683094
#[test]

crates/core/src/decoder/arm.rs

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -311,10 +311,12 @@ pub enum Instruction {
311311
rd: u8,
312312
rm: u8,
313313
},
314-
/// Wide register-extend (T2): {S,U}XT{B,H}.W Rd, Rm, ROR #rotate.
315-
/// `rotate` is 0/8/16/24 (applied to Rm before the extract).
314+
/// Wide register-extend (T2): {S,U}XT{B,H}.W and the extend-and-add
315+
/// {S,U}XTA{B,H}.W. `Rd = (Rn==0xF ? 0 : Rn) + extend(ROR(Rm, rotate))`.
316+
/// `rotate` is 0/8/16/24; `rn`==0xF means the plain extend (no add).
316317
ExtendW {
317318
rd: u8,
319+
rn: u8,
318320
rm: u8,
319321
rotate: u8,
320322
/// 0=SXTH, 1=UXTH, 4=SXTB, 5=UXTB (ARM op field h1[6:4]).
@@ -1474,20 +1476,28 @@ pub fn decode_thumb_32(h1: u16, h2: u16) -> Instruction {
14741476
}
14751477
}
14761478

1477-
// Register-extend, wide (T2): SXTH.W/UXTH.W/SXTB.W/UXTB.W Rd, Rm{, ROR #r}.
1478-
// h1 = 1111 1010 0 op 1111 (Rn=0xF, no add); op: 000=SXTH 001=UXTH
1479-
// 100=SXTB 101=UXTB (010/011 = SXTB16/UXTB16, not modeled).
1479+
// Register-extend, wide (T2): the plain {S,U}XT{B,H}.W (Rn=0xF) and the
1480+
// extend-and-add {S,U}XTA{B,H}.W (Rn!=0xF).
1481+
// h1 = 1111 1010 0 op nnnn ; op: 000=SXT(A)H 001=UXT(A)H 100=SXT(A)B
1482+
// 101=UXT(A)B (010/011 = ..B16, not modeled). nnnn = Rn (0xF = no add).
14801483
// h2 = 1111 dddd 10 rr mmmm (rr = rotate/8).
1481-
// clang emits e.g. `uxth.w r2, ip` = FA1F F28C when extending via a high
1482-
// register; without this the insn decoded to Unknown32 and was skipped,
1483-
// leaving the destination register stale.
1484-
if (h1 & 0xFF8F) == 0xFA0F && (h2 & 0xF080) == 0xF080 {
1484+
// clang emits e.g. `uxth.w r2, ip` = FA1F F28C (extend via high register)
1485+
// and `uxtah r6, r3, r0` = FA13 F680 (4 + path_len). Without this the insn
1486+
// decoded to Unknown32 and was skipped, leaving Rd stale.
1487+
if (h1 & 0xFF80) == 0xFA00 && (h2 & 0xF080) == 0xF080 {
1488+
let rn = (h1 & 0xF) as u8;
14851489
let rd = ((h2 >> 8) & 0xF) as u8;
14861490
let rm = (h2 & 0xF) as u8;
14871491
let rotate = (((h2 >> 4) & 0x3) * 8) as u8;
14881492
let op = ((h1 >> 4) & 0x7) as u8;
14891493
if op == 0b000 || op == 0b001 || op == 0b100 || op == 0b101 {
1490-
return Instruction::ExtendW { rd, rm, rotate, op };
1494+
return Instruction::ExtendW {
1495+
rd,
1496+
rn,
1497+
rm,
1498+
rotate,
1499+
op,
1500+
};
14911501
}
14921502
}
14931503

out_deploy_f10.log

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
F10 GATE FAIL

0 commit comments

Comments
 (0)