assembly - ARM 拇指中的表分支字节(TBB)的简单示例

标签 assembly arm thumb

我正在尝试弄清楚TBB在 ARM assembly 中如何工作的细节。 我只是想找出一个简单的示例,但无论我的代码如何进入无限循环或无法编译。

.syntax unified             
.thumb      

BranchTable_Byte:
 .byte 0 @; Case1 offset calculation
 .byte ((Case2-Case1)/2) @; Case2 offset calculation
 .byte ((Case3-Case1)/2) @; Case3 offset calculation

.text
.global example_TBB
.thumb_func
 example_TBB:
 mov r1, #1

 ADR.W r0, BranchTable_Byte
 TBB [r0, r1] @; R1 is the index, R0 is the base address of the branch table

 Case1:
    @; an instruction sequence follows
    mov r2, #1
    b endTBB
 Case2:
    @; an instruction sequence follows
    mov r3, #2
    b endTBB
 Case3:
    @; an instruction sequence follows
    mov r4, #3
    b endTBB

 endTBB:

 bx lr

我相信应该发生的情况是,由于 r1=1,tbb 操作代码应该分支到情况 2,但无论我使用它多久,我都会遇到无限循环和/或编译错误。

最佳答案

so.s

.globl _start
_start:
    bl example_TBB
    b .

tbb.s

.syntax unified
.thumb

BranchTable_Byte:
 .byte 0 @; Case1 offset calculation
 .byte ((Case2-Case1)/2) @; Case2 offset calculation
 .byte ((Case3-Case1)/2) @; Case3 offset calculation

.text
.global example_TBB
.thumb_func
 example_TBB:
 mov r1, #1

 ADR.W r0, BranchTable_Byte
 TBB [r0, r1] @; R1 is the index, R0 is the base address of the branch table

 Case1:
    @; an instruction sequence follows
    mov r0, #1
    b endTBB
 Case2:
    @; an instruction sequence follows
    mov r0, #2
    b endTBB
 Case3:
    @; an instruction sequence follows
    mov r0, #3
    b endTBB

 endTBB:

 bx lr

为文本与数据地址创建一个位置并不重要,这只是看看工具正在做什么:

arm-none-eabi-ld -Ttext=0x1000 -Tdata=0x2000 so.o tbb.o -o so.elf

00001000 <_start>:
    1000:   fb000000    blx 100a <example_TBB>
    1004:   eafffffe    b   1004 <_start+0x4>

00001008 <BranchTable_Byte>:
    1008:       svcmi   0x00060300

0000100a <example_TBB>:
    100a:   01f04f06    mvnseq  r4, r6, lsl #30
    100e:   af01        add r7, sp, #4
    1010:   08f2        lsrs    r2, r6, #3

就这样,有一个大问题。表中 3 个字节的数据如何容纳在两个字节中?

您的代码暗示您可能想这样做:

.syntax unified
.thumb
.data
BranchTable_Byte:
 .byte 0 @; Case1 offset calculation
 .byte ((Case2-Case1)/2) @; Case2 offset calculation
 .byte ((Case3-Case1)/2) @; Case3 offset calculation

.text
.global example_TBB
.thumb_func
 example_TBB:
 mov r1, #1

 ADR.W r0, BranchTable_Byte
 TBB [r0, r1] @; R1 is the index, R0 is the base address of the branch table

 Case1:
    @; an instruction sequence follows
    mov r0, #1
    b endTBB
 Case2:
    @; an instruction sequence follows
    mov r0, #2
    b endTBB
 Case3:
    @; an instruction sequence follows
    mov r0, #3
    b endTBB

 endTBB:

 bx lr

哇,这更糟糕(这是一个 adr,而不是加载地址):

00001000 <_start>:
    1000:   fa000000    blx 1008 <example_TBB>
    1004:   eafffffe    b   1004 <_start+0x4>

00001008 <example_TBB>:
    1008:   f04f 0101   mov.w   r1, #1
    100c:   f2af 0004   subw    r0, pc, #4
    1010:   e8d0 f001   tbb [r0, r1]

00001014 <Case1>:
    1014:   f04f 0001   mov.w   r0, #1
    1018:   e005        b.n 1026 <endTBB>

0000101a <Case2>:
    101a:   f04f 0002   mov.w   r0, #2
    101e:   e002        b.n 1026 <endTBB>

00001020 <Case3>:
    1020:   f04f 0003   mov.w   r0, #3
    1024:   e7ff        b.n 1026 <endTBB>

00001026 <endTBB>:
    1026:   4770        bx  lr

Disassembly of section .data:

00002000 <__data_start>:
    2000:   Address 0x0000000000002000 is out of bounds.

您的表格深度为 3 个字节,出于对齐目的,将其调整为四个字节

.syntax unified
.thumb
BranchTable_Byte:
 .byte 0 @; Case1 offset calculation
 .byte ((Case2-Case1)/2) @; Case2 offset calculation
 .byte ((Case3-Case1)/2) @; Case3 offset calculation
 .byte 0
 
.text
.global example_TBB
.thumb_func
 example_TBB:
 mov r1, #1

 ADR.W r0, BranchTable_Byte
 TBB [r0, r1] @; R1 is the index, R0 is the base address of the branch table

 Case1:
    @; an instruction sequence follows
    mov r0, #1
    b endTBB
 Case2:
    @; an instruction sequence follows
    mov r0, #2
    b endTBB
 Case3:
    @; an instruction sequence follows
    mov r0, #3
    b endTBB

 endTBB:

 bx lr

给出

00001000 <_start>:
    1000:   fa000001    blx 100c <example_TBB>
    1004:   eafffffe    b   1004 <_start+0x4>

00001008 <BranchTable_Byte>:
    1008:   00060300    andeq   r0, r6, r0, lsl #6

0000100c <example_TBB>:
    100c:   f04f 0101   mov.w   r1, #1
    1010:   f2af 000c   subw    r0, pc, #12
    1014:   e8d0 f001   tbb [r0, r1]

好多了:现在 4 个字节适合 4 个字节。那很好。但更好的是,如果您将数据内联到您应该对齐的代码中,或者将数据放在后面:

.syntax unified
.thumb
BranchTable_Byte:
 .byte 0 @; Case1 offset calculation
 .byte ((Case2-Case1)/2) @; Case2 offset calculation
 .byte ((Case3-Case1)/2) @; Case3 offset calculation

.text
.align
.global example_TBB
.thumb_func

 example_TBB:
 mov r1, #1
...

这也解决了这个问题:

00001000 <_start>:
    1000:   fa000001    blx 100c <example_TBB>
    1004:   eafffffe    b   1004 <_start+0x4>

00001008 <BranchTable_Byte>:
    1008:   00060300    andeq   r0, r6, r0, lsl #6

0000100c <example_TBB>:
    100c:   f04f 0101   mov.w   r1, #1
    1010:   f2af 000c   subw    r0, pc, #12
    1014:   e8d0 f001   tbb [r0, r1]

您可能希望您的表位于您指定的 .text 中。如果你把它放在.data中,那么你必须将它从闪存获取到RAM,假设这是一个微 Controller 。但您需要采取稍微不同的方式。

.syntax unified
.thumb

.data
BranchTable_Byte:
 .byte 0 @; Case1 offset calculation
 .byte ((Case2-Case1)/2) @; Case2 offset calculation
 .byte ((Case3-Case1)/2) @; Case3 offset calculation

.text
.global example_TBB
.thumb_func

 example_TBB:
 mov r1, #1

 ldr r0,=BranchTable_Byte
 TBB [r0, r1] @; R1 is the index, R0 is the base address of the branch table

 Case1:
    @; an instruction sequence follows
    mov r0, #1
    b endTBB
 Case2:
    @; an instruction sequence follows
    mov r0, #2
    b endTBB
 Case3:
    @; an instruction sequence follows
    mov r0, #3
    b endTBB

 endTBB:

 bx lr


Disassembly of section .text:

00001000 <_start>:
    1000:   fa000000    blx 1008 <example_TBB>
    1004:   eafffffe    b   1004 <_start+0x4>

00001008 <example_TBB>:
    1008:   f04f 0101   mov.w   r1, #1
    100c:   4806        ldr r0, [pc, #24]   ; (1028 <endTBB+0x4>)
    100e:   e8d0 f001   tbb [r0, r1]

00001012 <Case1>:
    1012:   f04f 0001   mov.w   r0, #1
    1016:   e005        b.n 1024 <endTBB>

00001018 <Case2>:
    1018:   f04f 0002   mov.w   r0, #2
    101c:   e002        b.n 1024 <endTBB>

0000101e <Case3>:
    101e:   f04f 0003   mov.w   r0, #3
    1022:   e7ff        b.n 1024 <endTBB>

00001024 <endTBB>:
    1024:   4770        bx  lr
    1026:   20000000    andcs   r0, r0, r0
    ...

Disassembly of section .data:

00002000 <__data_start>:
    2000:   Address 0x0000000000002000 is out of bounds.

你不讨厌他们这样做吗?

.syntax unified
.thumb

.data
BranchTable_Byte:
 .byte 0 @; Case1 offset calculation
 .byte ((Case2-Case1)/2) @; Case2 offset calculation
 .byte ((Case3-Case1)/2) @; Case3 offset calculation

.text
.global example_TBB
.thumb_func

 example_TBB:
 mov r1, #1

 ldr r0,btbadd
 TBB [r0, r1] @; R1 is the index, R0 is the base address of the branch table

 Case1:
    @; an instruction sequence follows
    mov r0, #1
    b endTBB
 Case2:
    @; an instruction sequence follows
    mov r0, #2
    b endTBB
 Case3:
    @; an instruction sequence follows
    mov r0, #3
    b endTBB

.align
btbadd: .word BranchTable_Byte

 endTBB:

 bx lr

就这种方法而言,这是更好的:

Disassembly of section .text:

00001000 <_start>:
    1000:   fa000000    blx 1008 <example_TBB>
    1004:   eafffffe    b   1004 <_start+0x4>

00001008 <example_TBB>:
    1008:   f04f 0101   mov.w   r1, #1
    100c:   4805        ldr r0, [pc, #20]   ; (1024 <btbadd>)
    100e:   e8d0 f001   tbb [r0, r1]

00001012 <Case1>:
    1012:   f04f 0001   mov.w   r0, #1
    1016:   e007        b.n 1028 <endTBB>

00001018 <Case2>:
    1018:   f04f 0002   mov.w   r0, #2
    101c:   e004        b.n 1028 <endTBB>

0000101e <Case3>:
    101e:   f04f 0003   mov.w   r0, #3
    1022:   e001        b.n 1028 <endTBB>

00001024 <btbadd>:
    1024:   00002000    andeq   r2, r0, r0

00001028 <endTBB>:
    1028:   4770        bx  lr
    102a:   46c0        nop         ; (mov r8, r8)

Disassembly of section .data:

00002000 <__data_start>:
    2000:   Address 0x0000000000002000 is out of bounds.

但是现在你已经有了 .data 来处理类似的事情,你不需要它是 .data。

请注意,如果您将其与已编译的代码链接,您的编译器可能符合arm调用约定,即您不能在函数中修改r4,您必须保留它。这就是我修改你的代码的原因(我想你是在我为你将它移植到gas时从我那里得到的?)

我忘记了 so.s 中的 .thumb。这很好,不是感兴趣的代码,不会在上面修复,但在下面你可能想在代码中添加一些更多的偏执,为什么不在它上面撒上 .aligns...

so.s

.thumb
.globl _start
_start:
    .word 0x20001000
    .word reset
    .word loop
    .word loop

.thumb_func
loop: b loop
.thumb_func
reset:
    mov r0,#1
    bl example_TBB
    b .

tbb.s

.syntax unified
.thumb

.align
BranchTable_Byte:
    .byte ((Case0-Case0)/2)
    .byte ((Case1-Case0)/2)
    .byte ((Case2-Case0)/2)
    .byte ((Case3-Case0)/2)

.align
.global example_TBB
.thumb_func
example_TBB:
    and r0,#3
    adr.w r1, BranchTable_Byte
    tbb [r1, r0]

.align
Case0:
    mov r0, #1
    b endTBB
Case1:
    mov r0, #2
    b endTBB
Case2:
    mov r0, #3
    b endTBB
Case3:
    mov r0, #4
    b endTBB

.align
endTBB:
    bx lr

给出

Disassembly of section .text:

08000000 <_start>:
 8000000:   20001000    andcs   r1, r0, r0
 8000004:   08000013    stmdaeq r0, {r0, r1, r4}
 8000008:   08000011    stmdaeq r0, {r0, r4}
 800000c:   08000011    stmdaeq r0, {r0, r4}

08000010 <loop>:
 8000010:   e7fe        b.n 8000010 <loop>

08000012 <reset>:
 8000012:   2001        movs    r0, #1
 8000014:   f000 f804   bl  8000020 <example_TBB>
 8000018:   e7fe        b.n 8000018 <reset+0x6>
    ...

0800001c <BranchTable_Byte>:
 800001c:   09060300    stmdbeq r6, {r8, r9}

08000020 <example_TBB>:
 8000020:   f000 0003   and.w   r0, r0, #3
 8000024:   f2af 010c   subw    r1, pc, #12
 8000028:   e8d1 f000   tbb [r1, r0]

0800002c <Case0>:
 800002c:   f04f 0001   mov.w   r0, #1
 8000030:   e008        b.n 8000044 <endTBB>

08000032 <Case1>:
 8000032:   f04f 0002   mov.w   r0, #2
 8000036:   e005        b.n 8000044 <endTBB>

08000038 <Case2>:
 8000038:   f04f 0003   mov.w   r0, #3
 800003c:   e002        b.n 8000044 <endTBB>

0800003e <Case3>:
 800003e:   f04f 0004   mov.w   r0, #4
 8000042:   e7ff        b.n 8000044 <endTBB>

08000044 <endTBB>:
 8000044:   4770        bx  lr
 8000046:   46c0        nop         ; (mov r8, r8)

这是一个完整的程序,您可以在 stm32 上运行它,并在完成后使用 openocd 停止并检查寄存器,以查看 r0 设置为什么。你也可以这样做

.syntax unified
.thumb
.globl _start
_start:
    mov r0,#1
    bl example_TBB
    b .

.align
BranchTable_Byte:
    .byte ((Case0-Case0)/2)
    .byte ((Case1-Case0)/2)
    .byte ((Case2-Case0)/2)
    .byte ((Case3-Case0)/2)

.align
.global example_TBB
.thumb_func
example_TBB:
    and r0,#3
    adr.w r1, BranchTable_Byte
    tbb [r1, r0]

.align
Case0:
    mov r0, #1
    b endTBB
Case1:
    mov r0, #2
    b endTBB
Case2:
    mov r0, #3
    b endTBB
Case3:
    mov r0, #4
    b endTBB

.align
endTBB:
    bx lr

内存地址为 0x20000000 的链接

Disassembly of section .text:

20000000 <_start>:
20000000:   f04f 0001   mov.w   r0, #1
20000004:   f000 f804   bl  20000010 <example_TBB>
20000008:   e7fe        b.n 20000008 <_start+0x8>
2000000a:   46c0        nop         ; (mov r8, r8)

2000000c <BranchTable_Byte>:
2000000c:   09060300    stmdbeq r6, {r8, r9}

20000010 <example_TBB>:
20000010:   f000 0003   and.w   r0, r0, #3
20000014:   f2af 010c   subw    r1, pc, #12
20000018:   e8d1 f000   tbb [r1, r0]

2000001c <Case0>:
2000001c:   f04f 0001   mov.w   r0, #1
20000020:   e008        b.n 20000034 <endTBB>

20000022 <Case1>:
20000022:   f04f 0002   mov.w   r0, #2
20000026:   e005        b.n 20000034 <endTBB>

20000028 <Case2>:
20000028:   f04f 0003   mov.w   r0, #3
2000002c:   e002        b.n 20000034 <endTBB>

2000002e <Case3>:
2000002e:   f04f 0004   mov.w   r0, #4
20000032:   e7ff        b.n 20000034 <endTBB>

20000034 <endTBB>:
20000034:   4770        bx  lr
20000036:   46c0        nop         

然后你可以加载它,运行它,停止并检查来自 openocd 的 r0 与你的 STM32 的对话...

关于assembly - ARM 拇指中的表分支字节(TBB)的简单示例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52684265/

相关文章:

将 C 转换为 MIPS - 嵌套数组

arm - 对于 ARM Aarch64 的 NEON 编码,如何将寄存器压入堆栈?似乎 STMFD 不是 Aarch64 指令集的一部分?

用 NEON 改进的代码

linux - 如何仅编译 ARM32 二进制文件(没有拇指)

c - 执行 arm blx rx 时出现 EXC_BAD_ACCESS

arm - 使用 GDB 在 Cortex-M 上进行软重置

使用 Easy 68K (68000) 组装范围内的随机数

c - 使用 NASM 构建 KMP 前缀

c - CMP+JE 是否比单个 MUL 消耗更多的时钟周期?

c++ - `if` 语句在取模之前和分配操作之前是多余的吗?