手臂拇指中 Table 分支字节 (TBB) 的简单示例

Simple example of Table Branch Byte (TBB) in arm thumb

我想弄清楚 TBB 在手臂装配中的工作原理。 我只是想找出一个简单的例子,但无论我的代码进入无限循环还是无法编译。

.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,但是无论我玩了多长时间,我都会遇到无限循环 and/or 编译错误。

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

你去吧,有一个大问题。 table 中的 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

哇那更糟(它是一个地址而不是加载地址)

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.

你的 table 是 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]

您可能希望您的 table 位于您指定的 .text 中。如果你把它放在 .data 中,那么假设这是一个微控制器,你必须将它从闪存中获取到 ram。但是你需要做一些稍微不同的事情。

.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。

请注意,如果您 link 使用已编译的代码将其加入其中,您的编译器可能符合 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

link 内存在 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 对话...