arm 对数据表和指令表的访问

ARM 111浏览

以下介绍arm 对数据表的访问和指令表的访问
数据表:ldr      r1,[r5,r4,lsl #2]        //索引寻址(取内存)
指令表:addls    pc,pc,r0,lsl #2        //相对寻址(计算数据)
arm 对数据表的访问:
1. c 程序。
#include <stdio.h>
int a[5]={
1,2,3,4,5
};

int main(void)
{
    int i;  
    for(i=0; i<5; i++) ?
    {
        printf("data is %d/n",a[i]);
    }
    return 0;  
}

2. 汇编程序
main    [0xe92d4038] * stmfd    r13!,{r3-r5,r14}
000080ac    [0xe59f5020]   ldr      r5,0x000080d4 ; = #0x0000a630 // ldr 间址寻址
000080b0    [0xe3a04000]   mov      r4,#0
000080b4    [0xe7951104]   ldr      r1,[r5,r4,lsl #2]     // 从表中取到数据
000080b8    [0xe28f0018]   add      r0,pc,#0x18 ; #0x80d8 // 寻址常量字符串
000080bc    [0xeb00000a]   bl       _printf                // 调用printf函数
000080c0    [0xe2844001]   add      r4,r4,#1            // 循环次数加1
000080c4    [0xe3540005]   cmp      r4,#5               // 小于5次,循环
000080c8    [0xbafffff9]   blt      0x80b4  ; (main + 0xc)
000080cc    [0xe3a00000]   mov      r0,#0                //return 0
000080d0    [0xe8bd8038]   ldmfd    r13!,{r3-r5,pc}
000080d4    [0x0000a630]   dcd      0x0000a630  0...    // 存储地址,相当于指针
000080d8    [0x61746164]   dcd      0x61746164  data
000080dc    [0x20736920]   dcd      0x20736920   is
000080e0    [0x000a6425]   dcd      0x000a6425  %d..
。。。。。。

0000a630    [0x00000001]   dcd      0x00000001  ....
0000a634    [0x00000002]   dcd      0x00000002  ....
0000a638    [0x00000003]   dcd      0x00000003  ....
0000a63c    [0x00000004]   dcd      0x00000004  ....
0000a640    [0x00000005]   dcd      0x00000005  ....
简洁呀,arm 汇编!
从表中取到数据,当然是基址加变址,但是,这里把基址送给一个寄存器,变址用两外一个寄存器加简单运算。
 ldr      r1,[r5,r4,lsl #2]
函数调用用寄存器传递参数。
函数返回(弹栈),压栈时保留了link寄存器,弹栈时返回给了pc 寄存器。
; ---------------------------------------------------------------------------
我们再看一下arm 的switch-case 跳转表。 发现其跳转表是由跳转指令组成,是一个跳转指令表
散转到各函数处,只有设置好PC 指针就可以了。
#include <stdio.h>
int a[5]={
1,2,3,4,5
};
void test_switch(int i);

int main(void)
{
[0xe92d4008] * stmfd    r13!,{r3,r14}
    test_switch(3); I
[0xe3a00003]   mov      r0,#3
[0xebffffdf]   bl       test_switch
    return 0;  ;
[0xe3a00000]   mov      r0,#0
}
[0xe8bd8008]   ldmfd    r13!,{r3,pc}

void test_switch(int i)
{
    switch(i) { 4
[0xe3500004]   cmp      r0,#4
[0x908ff100]   addls    pc,pc,r0,lsl #2    // 相对寻址。r0 是变量。更改pc 值
[0xea00000e]   b        0x80f0  ; (test_switch + 0x48) // 跳转指令表。
[0xea000003]   b        0x80c8  ; (test_switch + 0x20)
[0xea000004]   b        0x80d0  ; (test_switch + 0x28)
[0xea000005]   b        0x80d8  ; (test_switch + 0x30)
[0xea000006]   b        0x80e0  ; (test_switch + 0x38)
[0xea000007]   b        0x80e8  ; (test_switch + 0x40)
    case 0:
        printf("it's 0/n");  l u
[0xe28f0024]   add      r0,pc,#0x24 ; #0x80f4
[0xea000019]   b        _printf            // 这家伙很懒,连break也不翻译了,而是借用了printf 的返回指令。
        break;                          // 因为编译器发现正常是一个两级返回,所以把它优化为一级返回。
    case 1:
        printf("it's 1/n");  / C
[0xe28f0024]   add      r0,pc,#0x24 ; #0x80fc
[0xea000017]   b        _printf
        break;
    case 2:
        printf("it's 2/n");    
[0xe28f0024]   add      r0,pc,#0x24 ; #0x8104
[0xea000015]   b        _printf
        break;     
    case 3:  
        printf("it's 3/n");     
[0xe28f0024]   add      r0,pc,#0x24 ; #0x810c
[0xea000013]   b        _printf
        break;     
    case 4:  
        printf("it's 4/n");     
[0xe28f0024]   add      r0,pc,#0x24 ; #0x8114
[0xea000011]   b        _printf
        break;     
    default:  
        ;     
    }  
}
[0xe1a0f00e]   mov      pc,r14        // 函数返回
[0x73277469]   dcd      0x73277469  it's // 常数表。
[0x000a3020]   dcd      0x000a3020   0..
[0x73277469]   dcd      0x73277469  it's
[0x000a3120]   dcd      0x000a3120   1..
[0x73277469]   dcd      0x73277469  it's
[0x000a3220]   dcd      0x000a3220   2..
[0x73277469]   dcd      0x73277469  it's
[0x000a3320]   dcd      0x000a3320   3..
[0x73277469]   dcd      0x73277469  it's
[0x000a3420]   dcd      0x000a3420   4..