Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions document/notes/017-e2-correctness-debt-tier1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# 017 — E2 Tier 1:残余正确性债清理(ADC/SBC 全形式 + SDIV/0)

> E2「指令 conformance + 清债」里程碑 · Tier 1。第二波外设中断端到端(06 C1/C2/C3)收口后,焦点回到项目 #1 支柱「CPU/诊断可信度」。本批清掉覆盖矩阵名义「已完成」、实测仍有 bug 的三处残余正确性债。ctest **312/312 绿**(301 + 11 新)。

## 背景:文档漂移下的「真债」定位

读-only 审计(4 agent 并行 + 人工逐条核实)发现:覆盖矩阵 §1/§6 头表过时(描述 T0 前状态、行号全错),把已修项仍标 partial/missing;而 06 §真实性债#5 点名的 SVC/CLZ 早在 T2/T3 落地。**真正的残余债只有三处**,且两处在「.W 已实现」的假象下藏着:

## 三处真债

### T1a · 32-bit `adc.w`/`sbc.w` shifted-register 形式整条缺失(F32-6)
`t32_dataproc_reg` 的 `switch(op)`(`cortex_m3_thumb32_dataproc.cpp:179`)只有 `0/1/2/3/4/8/13/14`,**无 case 10(ADC)/11(SBC)** → `adc.w/sbc.w reg,reg,shift` 落 `default → IllegalInstruction`。矩阵/旧审计误判「.W 已实现」(那只指 imm 形式 :86-112)。补 case 10/11。
- objdump 确认编码:`adcs.w r3,r1,r2,lsl#4 = 0xEB51 0x1302`,`sbcs.w = 0xEB71`(S 位 = hw1 bit4;**adc.w(S=0) 不写 flag**,flag 测试必须用 adcs.w)。

### T1b · 16-bit `adcs`/`sbcs`(op 5/6)漏 C/V(F16-3)
`cortex_m3_thumb16.cpp` op5/6 算对了 result(含进位),但只走共享 epilogue 的 `update_nz(result)`(`:343`),**从不写 C/V**。op5/6 改为早返回 + 显式置全 flag。

### T1c · INT_MIN/-1 有符号溢出(UB)guard;SDIV/0 语义由 oracle 钉死
两件事:
1. **`INT_MIN / -1` 是 C 有符号溢出(UB)** —— 旧 `a/b` 在该输入下是 UB(可能崩溃/乱值)。ARMv7-M 饱和到 INT_MIN(`0x80000000`)。加 guard(与 QEMU 一致)。
2. **SDIV/0 结果 = 0(双符号)**。初版误记成「负被除数→INT_MIN」并实现成 INT_MIN —— **被 Tier-2 QEMU oracle(`scripts/qemu_cortex_m3_oracle.sh`,mps2-an385)当场否决**:Cortex-M3 SDIV/0 对正负被除数都返回 0。已改回 0;UDIV/0 = 0。这正是 oracle 存在的意义(「不靠记忆」,以工具链证据为准)。

## 设计点

- **进位感知 flag helper(替代「合并操作数」trick)**:imm 形式旧法把 `op2+Cin` 当 `b` 传给 `update_flags(Add/Sub)`,在 `op2=0xFFFFFFFF` 且有 Cin 时 `b` 环绕到 0,C 算错。新增 `set_adc_flags`/`set_sbc_flags`(64 位和算 C,标准公式算 V),三处(32-imm/32-reg/16-reg)统一改用,SBC 用 `a + ~b + cin` 的进位出 = NOT-borrow。彻底消除环绕边角。
- SBC 的 V:由 `a + ~b + cin` 加法溢出推导,化简为 `((a^b)&(a^result))&sign`,与 SUB 一致(已数学验证)。
- **`DIV_0_TRP` trap 暂不建模**:configurable-fault 特性(06 列 ~60% 债,涉及 UsageFault 路径 + CCR 可写)。复位默认 `DIV_0_TRP==0` 是固件实际所见,故 predictable-result(=0)即「正确模拟」当前态;trap 作单独 configurable-fault 批跟进。
- **QEMU oracle 已跑通并锁定全部 Tier-1 语义**(`scripts/qemu_cortex_m3_oracle.sh`):SDIV/0=0、INT_MIN/-1=INT_MIN、ADC/SBC 全形式 result+flag 全与 `qemu-system-arm -M mps2-an385` 一致(DIV/ALU/xPSR 三行逐字对齐)。差分不再是「待办」而是「已过」。
- **flag 测试必须 S=1**:首版测试误用 `adc.w`(S=0)断言 flag → 全红;改 `adcs.w`/`sbcs.w`。教训:objdump 看 `adc.w` vs `adcs.w` 区分 S 位,断言 flag 前先确认。

## 验证

- 11 新单测(ADC.W shifted-reg carry/overflow、SBC.W borrow、16-bit ADCS/SBCS、SDIV/0 neg/nonneg/INT_MIN-over-(-1)/normal、UDIV/0),全 `arm-none-eabi-as` + objdump 定编码,断言**具体 result 值 + N/Z/C/V 位**(经 `MRS R0,APSR`),非 roundtrip。
- `ctest` 全量 **312/312 绿**,固件 E2E(3 AC6 + gcc hal_uart)/ CLI / 中断抢占无回归。

## 成果与后续

Tier 1 把覆盖里程碑「名义完成」下的最后三处正确性 bug 清零;ADC/SBC 全形式(16-reg / 32-imm / 32-shifted-reg)flag 统一正确,**经 QEMU 差分逐字确认**。Tier 2(同分支):差分 oracle 已落地并通过(见上);`-O0/-O2/-Oz` 固件回归门 + armcc 语料构建 pwsh 脚本(`test/firmware/armcc/build_corpus_opt.ps1`,NTFS 端用户执行)见 notes 018。
38 changes: 38 additions & 0 deletions document/notes/018-e2-tier2-oracle-optlevel-gate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# 018 — E2 Tier 2:QEMU 差分 oracle + 优化级固件回归门

> E2 conformance 里程碑 · Tier 2(与 Tier 1 同分支 `feat/e2-conformance`)。06/02 把「指令 conformance」定为可信度支柱的关键,本批把它从「-O0/-O2/-Os + 差分 oracle」的**设计**落成**可执行物**。两件:(1) QEMU Cortex-M3 差分 oracle 跑通并锁定 Tier-1 语义(notes 017);(2) armcc 多优化级语料构建 pwsh 脚本交付(NTFS 端,用户执行)。

## 1. QEMU 差分 oracle(已落地 + 通过)

`scripts/qemu_cortex_m3_oracle.sh`:汇编一个微型 Thumb 程序(SDIV/UDIV 四边角 + ADC/SBC shifted-reg 四例带 flag),在 `qemu-system-arm -M mps2-an385`(Cortex-M3)下 `-S -gdb tcp::PORT` 挂起,`arm-none-eabi-gdb` 连上 `break done; continue`,dump 结果区(0x2000/0x2010)与 xPSR 快照区(0x2020),与期望逐行比对。

- **本机工具链就绪**:`qemu-system-arm` 11.0.1 + `arm-none-eabi-gdb` 均在 `/usr/sbin`(装机可行性 = 已确认,不再「待评估」)。
- **结果(notes 017 已引)**:DIV/ALU/xPSR 三行与 QEMU 逐字一致。**oracle 当场否决了 Tier-1 初版「SDIV/0 负→INT_MIN」的误记**,改成正确的 SDIV/0=0(双符号)。这是「不靠记忆、以工具链为准」的直接收益,也是 oracle 存在的最佳广告。
- **坑(全趟过,记给后续)**:
- QEMU `-monitor stdio` 对 piped 输入做 readline 行编辑,`pmemsave` 的文件名参数被解析成表达式报 `invalid char 't'` —— **改走 gdbstub + gdb 读内存**,比 monitor pmemsave 稳得多。
- `arm-none-eabi-ld` 链接脚本**必须有空格**(`SECTIONS { . = 0x0; .vectors : {...} }`,紧凑无空格版 syntax error)。
- 分支目标 label **必须前缀 `.thumb_func`**(否则 `Unknown destination type (ARM/Thumb)` 重定位错)。`.thumb_func` 只标记**下一个**符号,每个被 branch/.word 引用的 thumb 入口前都要加。

## 2. 优化级固件回归门(设计 + pwsh 脚本交付)

### 动机
不同 `-O` 级发射不同 Thumb-2 指令组合(-O2 可能发 `adc.w`/`sbc.w`/`sdiv` 而 -O0 不发)。只跑单优化级语料(notes 008 的 3 份 AC6 .axf),无法证明「指令对的」在 codegen 变体下成立。多优化级语料是最便宜的信任放大器(02 §验收 line 76)。

### 脚本:`test/firmware/armcc/build_corpus_opt.ps1`
PowerShell,把现有 3 个 CubeF1 示例(GPIO/TIM/UART)在 AC6 的 **-O0/-O2/-Oz** 三级各编一份,产出 `nucleo_f103rb_<ex>.ac6-<opt>.axf`。
- 复用 REGENERATE.md 的 AC5→AC6 三补丁(`uAC6` / 删 `--C99` / 删 `v6Lang` 族),**新增第 4 补丁**:清 MiscControls 里旧 `-O` token,追加 `-O<level>`(armclang 直接认)。**注意:AC6 无 `-Os`;最小体积是 `-Oz`**(AC5/旧 armcc 的 `-Os` 在 AC6 映射到 `-Oz`)。脚本内 `Os→Oz`,注释说明。
- **必须在 Windows NTFS 跑**(WSL 9p 建不了 Keil `.__i`);产物落 `D:\mf\out\`,用户从 WSL `cp /mnt/d/mf/out/*.ac6-*.axf test/firmware/armcc/` 回库。
- opt 级若被 Keil 结构化 Optimization 字段覆盖(非 MiscControls),在 GUI 同步设或在脚本里调 —— 这块以用户(Keil 侧专家)实测为准,脚本为权威起点。
- `-DryRun` 只 patch+打印不编,便于先核对 .uvprojx 改动。
- **结构事实(实测 vendored `third_party/STM32CubeF1`,纠正 REGENERATE 口径)**:工程文件是 **`Project.uvprojx`**(不是 `STM32F103RB_Nucleo.uvprojx`);`<TargetName>/<OutputName>=STM32F103RB_Nucleo`,`<OutputDirectory>STM32F103RB_Nucleo\` → 产物 `MDK-ARM\STM32F103RB_Nucleo\STM32F103RB_Nucleo.axf`;include 路径 `../../../../../../Drivers` 是 **6 级**(不是 5),故 Drivers/ 与 Projects/ 必须在 CubeF1 根顶层。脚本据此修了初版两 bug(工程名错 + 跨优化级产物撞),并**按优化级复制整个 MDK-ARM 目录**(`MDK-ARM-<opt>`)做产物/对象隔离;opt 标志经 XML **只注入 Cads(C 编译器)** 的 MiscControls(不碰汇编)。
- **重建 D:\mf**:`scripts/prepare_corpus.sh`(WSL,写 `/mnt/d/mf`)从 vendored 树拷最小子树(Drivers/BSP + HAL_Driver + CMSIS/{Device,Core,Include},**砍掉 DSP/Lib/docs/NN 等 ~111M 无关大块**;3 个 Examples),保持 6 级深度。`/mnt/d` 从 WSL 可读写(只有 Keil *build* 必须 Windows 原生跑)。实测重建后 22M,深度校验通过。

### 门禁集成(Tier 2 收尾,待 .axf 就位)
`test/test_firmware_armcc.cpp` 每个 opt 变体加一个 `TEST(FirmwareArmcc, <Ex>Opt<O>BootsClean)`,加载对应 `.ac6-<opt>.axf` 跑 `reset→main→while(1)` 断言 0-fault(复用现有 `Stm32f103Soc::load_elf`)。.axf 由用户用脚本产出后提交(CI 无 Keil,二进制入库,同现有 3 份)。这部分代码待 .axf 落地再写,避免空 fixture。

## 3. 状态

- ✅ QEMU oracle:落地、跑通、Tier-1 全语义锁定(含纠错 SDIV/0)。
- ✅ pwsh 构建脚本:交付,待用户 NTFS 端执行产 .axf。
- ⬜ opt 级门禁测试:待 .axf 就位后补 `test_firmware_armcc` 用例(本批不阻塞)。
- 余:`DIV_0_TRP` trap(configurable-fault 批)、全量 matrix §6 re-baseline(文档债),均非本批。
7 changes: 7 additions & 0 deletions include/arch/arm/cortex_m3/cortex_m3.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,13 @@ class CortexM3CPU : public CPU {
enum class FlagPostOperation { Add, Sub };
void update_flags(FlagPostOperation p, data_t a, data_t b, data_t result);

// ADC/SBC flag updates with an explicit carry-in. Unlike update_flags(Add/
// Sub), C and V are derived from a 64-bit sum so the carry-in folds in
// correctly even when the second operand is 0xFFFFFFFF (folding Cin into the
// operand before a plain add/sub would wrap there and mis-report C).
void set_adc_flags(data_t a, data_t b, data_t cin, data_t result);
void set_sbc_flags(data_t a, data_t b, data_t cin, data_t result);

/* If we get false, then we need to jump */
bool condition_need_execute(uint8_t command);
CPUExpected<void> push_stack(data_t val);
Expand Down
55 changes: 55 additions & 0 deletions scripts/prepare_corpus.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#!/usr/bin/env bash
# prepare_corpus.sh — rebuild the minimal CubeF1 subtree on D:\mf for the
# armcc/AC6 firmware corpus build.
# ============================================================================
# Copies the vendored third_party/STM32CubeF1 minimal subtree to
# /mnt/d/mf/STM32CubeF1 (== D:\mf\STM32CubeF1), PRESERVING the directory depth
# the .uvprojx files expect: each example's includes use `../../../../../../Drivers`
# (6 levels up from Projects/.../<ex>/MDK-ARM to the CubeF1 root where Drivers
# lives). So Drivers/ and Projects/ MUST sit at the top of D:\mf\STM32CubeF1.
#
# Run from WSL (writes to NTFS via /mnt/d, which is writable; only the Keil
# *build* must run native on Windows via build_corpus_opt.ps1 — REGENERATE.md).
#
# CMSIS is trimmed: keep Device + Core + Include (~14M), drop DSP/Lib/docs/NN/
# Core_A/RTOS/RTOS2 (~111M of build-irrelevant bulk). CMSIS Core headers also
# arrive via the D:\MDK-Pack CMSIS pack at build time.
set -euo pipefail

SRC="/home/charliechen/micro-forge/third_party/STM32CubeF1"
DST="/mnt/d/mf/STM32CubeF1"
EXAMPLES=(GPIO/GPIO_IOToggle TIM/TIM_TimeBase UART/UART_Printf)
EXBASE="Projects/STM32F103RB-Nucleo/Examples"

[[ -d "$SRC/Drivers" ]] || { echo "SRC missing: $SRC"; exit 1; }
if [[ -e "$DST" && -n "$(ls -A "$DST" 2>/dev/null)" ]]; then
echo "DST exists and is non-empty: $DST"; echo " rm -rf '$DST' first, or pass --force"; [[ "${1:-}" == "--force" ]] && rm -rf "$DST" || exit 1
fi

echo ">> SRC = $SRC"; echo ">> DST = $DST"
mkdir -p "$DST/Drivers/CMSIS" "$DST/$EXBASE"

echo ">> copying Drivers ..."
cp -a "$SRC/Drivers/BSP" "$DST/Drivers/"
cp -a "$SRC/Drivers/STM32F1xx_HAL_Driver" "$DST/Drivers/"
cp -a "$SRC/Drivers/CMSIS/Device" "$DST/Drivers/CMSIS/"
cp -a "$SRC/Drivers/CMSIS/Core" "$DST/Drivers/CMSIS/"
cp -a "$SRC/Drivers/CMSIS/Include" "$DST/Drivers/CMSIS/"
cp -a "$SRC/Drivers/CMSIS/LICENSE.txt" "$DST/Drivers/CMSIS/" 2>/dev/null || true

echo ">> copying Examples ..."
for ex in "${EXAMPLES[@]}"; do
mkdir -p "$DST/$EXBASE/$(dirname "$ex")"
cp -a "$SRC/$EXBASE/$ex" "$DST/$EXBASE/$ex"
done

echo ">> verifying 6-level depth resolves to Drivers ..."
probe="$DST/$EXBASE/GPIO/GPIO_IOToggle/MDK-ARM"
ok=1
for _ in 1 2 3 4 5 6; do probe="$(dirname "$probe")"; done
[[ -d "$probe/Drivers" ]] && echo " OK: $probe/Drivers exists" || { echo " FAIL: Drivers not at CubeF1 root"; ok=0; }
[[ -f "$DST/$EXBASE/GPIO/GPIO_IOToggle/MDK-ARM/Project.uvprojx" ]] && echo " OK: Project.uvprojx present" || { echo " FAIL: Project.uvprojx missing"; ok=0; }

echo ">> sizes"; du -sh "$DST" "$DST/Drivers" 2>/dev/null
echo ""; echo "Done. Next (on Windows): pwsh test/firmware/armcc/build_corpus_opt.ps1"
[[ $ok -eq 1 ]] || exit 2
77 changes: 77 additions & 0 deletions scripts/qemu_cortex_m3_oracle.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#!/usr/bin/env bash
# qemu_cortex_m3_oracle.sh — differential oracle vs QEMU Cortex-M3 (mps2-an385)
# ============================================================================
# Validates micro-forge's Tier-1 fixes against QEMU's Cortex-M3 model:
# * SDIV/UDIV divide semantics (incl. INT_MIN/-1 saturation)
# * ADC/SBC shifted-register result + N/Z/C/V flags (the form that was
# previously IllegalInstruction)
#
# Assembles a tiny Thumb program, runs it under qemu-system-arm paused with a
# gdbstub, breaks at the done loop, and dumps the result/xPSR buffers.
# Requires arm-none-eabi-as/-ld + qemu-system-arm + arm-none-eabi-gdb.
#
# This oracle already caught one spec error pre-merge: SDIV/0 returns 0 (not
# INT_MIN) for a negative dividend on Cortex-M3. See document/notes/017.
set -euo pipefail

WORK="$(mktemp -d)"; trap 'rm -rf "$WORK"' EXIT
PORT=12345
ASM="$WORK/o.s"; LDS="$WORK/o.ld"; ELF="$WORK/o.elf"

cat > "$ASM" <<'ASM'
.syntax unified
.cpu cortex-m3
.thumb
.section .vectors,"ax",%progbits
.thumb_func
vectors: .word 0x00008000 ; .word Reset_Handler+1
.section .text,"ax",%progbits
.thumb_func
.global done
Reset_Handler:
ldr r4, =0x00002000 @ SDIV/UDIV results (4 words)
ldr r0, =0xFFFFFFFF ; mov r1,#0 ; sdiv r2,r0,r1 ; str r2,[r4],#4 @ -1/0
mov r0,#5 ; sdiv r2,r0,r1 ; str r2,[r4],#4 @ 5/0
ldr r0, =0x80000000 ; ldr r1,=0xFFFFFFFF ; sdiv r2,r0,r1 ; str r2,[r4],#4 @ INT_MIN/-1
mov r0,#123 ; mov r1,#0 ; udiv r2,r0,r1 ; str r2,[r4],#4 @ udiv 123/0
ldr r6, =0x00002010 @ ADC/SBC results
ldr r7, =0x00002020 @ xPSR snapshots (N=31 Z=30 C=29 V=28)
ldr r5, =0x20000000 ; msr apsr_nzcvq, r5 @ C=1
ldr r0, =0xFFFFFFFF ; mov r1,#0 ; adcs.w r2,r0,r1 ; str r2,[r6],#4 ; mrs r3,apsr ; str r3,[r7],#4
mov r5,#0 ; msr apsr_nzcvq, r5 @ C=0
ldr r0, =0x7FFFFFFF ; mov r1,#1 ; adcs.w r2,r0,r1 ; str r2,[r6],#4 ; mrs r3,apsr ; str r3,[r7],#4
ldr r5, =0x20000000 ; msr apsr_nzcvq, r5 @ C=1
mov r0,#5 ; mov r1,#1 ; sbcs.w r2,r0,r1 ; str r2,[r6],#4 ; mrs r3,apsr ; str r3,[r7],#4
mov r0,#5 ; mov r1,#7 ; sbcs.w r2,r0,r1 ; str r2,[r6],#4 ; mrs r3,apsr ; str r3,[r7],#4
.thumb_func
done: b done
ASM

cat > "$LDS" <<'LDS'
SECTIONS {
. = 0x0;
.vectors : { *(.vectors) }
.text : { *(.text) }
}
LDS

arm-none-eabi-as -mcpu=cortex-m3 -o "$WORK/o.o" "$ASM"
arm-none-eabi-ld -T "$LDS" -o "$ELF" "$WORK/o.o"

qemu-system-arm -M mps2-an385 -kernel "$ELF" -S -gdb tcp::$PORT -display none &
QPID=$!
trap 'kill $QPID 2>/dev/null || true; rm -rf "$WORK"' EXIT
sleep 0.7

arm-none-eabi-gdb -q -batch -nx \
-ex "target remote :$PORT" -ex 'break done' -ex 'continue' \
-ex 'printf "DIV : %08x %08x %08x %08x\n", *(int*)0x2000,*(int*)0x2004,*(int*)0x2008,*(int*)0x200c' \
-ex 'printf "ALU : %08x %08x %08x %08x\n", *(int*)0x2010,*(int*)0x2014,*(int*)0x2018,*(int*)0x201c' \
-ex 'printf "xPSR : %08x %08x %08x %08x\n", *(int*)0x2020,*(int*)0x2024,*(int*)0x2028,*(int*)0x202c' \
-ex 'kill' -ex 'quit' "$ELF" 2>&1 | grep -E '^(DIV|ALU|xPSR)'

cat <<'EXP'
expect DIV : 00000000 00000000 80000000 00000000 (SDIV/0=0 both signs; INT_MIN/-1=INT_MIN; udiv/0=0)
expect ALU : 00000000 80000000 00000004 fffffffe (adc 0xFFFFFFFF+0+C1=0; adc 7FFFFFFF+1=80000000; sbc 5-1=4; sbc 5-7=-2)
expect xPSR: 60000000 90000000 20000000 80000000 (C+Z / N+V / C / N) — top nibble only
EXP
38 changes: 38 additions & 0 deletions src/arch/arm/cortex_m3/cortex_m3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,44 @@ void CortexM3CPU::update_flags(FlagPostOperation p, data_t a, data_t b,
}
}

void CortexM3CPU::set_adc_flags(data_t a, data_t b, data_t cin, data_t result) {
// ADC = a + b + cin. Carry-out is bit 32 of the full sum; V is signed
// overflow (operands share sign, result flips it).
xpsr_ &= ~(PSR_N | PSR_Z | PSR_C | PSR_V);
if (result & 0x80000000u) {
xpsr_ |= PSR_N;
}
if (result == 0) {
xpsr_ |= PSR_Z;
}
uint64_t sum = static_cast<uint64_t>(a) + b + cin;
if (sum >> 32) {
xpsr_ |= PSR_C;
}
if (((~(a ^ b)) & (a ^ result)) & 0x80000000u) {
xpsr_ |= PSR_V;
}
}

void CortexM3CPU::set_sbc_flags(data_t a, data_t b, data_t cin, data_t result) {
// SBC = a - b - !cin == a + ~b + cin (mod 2^32). The carry-out of that sum
// is the ARM "no borrow" C flag; V is signed overflow of a - b.
xpsr_ &= ~(PSR_N | PSR_Z | PSR_C | PSR_V);
if (result & 0x80000000u) {
xpsr_ |= PSR_N;
}
if (result == 0) {
xpsr_ |= PSR_Z;
}
uint64_t sum = static_cast<uint64_t>(a) + (~b) + cin;
if (sum >> 32) {
xpsr_ |= PSR_C;
}
if (((a ^ b) & (a ^ result)) & 0x80000000u) {
xpsr_ |= PSR_V;
}
}

bool CortexM3CPU::condition_need_execute(uint8_t c) {
bool N = xpsr_ & PSR_N;
bool Z = xpsr_ & PSR_Z;
Expand Down
26 changes: 20 additions & 6 deletions src/arch/arm/cortex_m3/cortex_m3_thumb16.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -297,12 +297,26 @@ CPU::CPUExpected<void> CortexM3CPU::execute_16bit(uint16_t insn) {
shift_carry = s.carry;
break;
}
case 0x5:
result = a + b + ((xpsr_ & PSR_C) ? 1 : 0);
break;
case 0x6:
result = a - b - ((xpsr_ & PSR_C) ? 0 : 1);
break;
case 0x5: { // ADCS: a + b + C — full N/Z/C/V (was update_nz only)
data_t cin = (xpsr_ & PSR_C) ? 1u : 0u;
result = a + b + cin;
auto res = wr(rd, result);
if (!res) {
return res;
}
set_adc_flags(a, b, cin, result);
return {};
}
case 0x6: { // SBCS: a - b - !C — full N/Z/C/V (was update_nz only)
data_t cin = (xpsr_ & PSR_C) ? 1u : 0u;
result = a - b - ((xpsr_ & PSR_C) ? 0u : 1u);
auto res = wr(rd, result);
if (!res) {
return res;
}
set_sbc_flags(a, b, cin, result);
return {};
}
case 0x7: { // ROR register
auto s = barrel_shift(3, a, b & 0xFF, (xpsr_ & PSR_C) != 0);
result = s.value;
Expand Down
Loading
Loading