TX2440 裸跑实验-汇编C语言混编(ADS1.2编译) 进阶(二)

TX2440 裸跑实验-汇编C语言混编(ADS1.2编译) 进阶(二)

一.实验目的:

理解汇编的编程结构,有助于学习ARM汇编与C语言的调用关系,熟悉堆栈的调用关系.

二.实验环境:

TX2440平台

三.实验工具:

ADS1.2 + AXD + JLINK调试

四.实验步骤:

1.在ads 上建立新工程,增加两个源文件call_C_func.s 和 func.c

1)call_C_func.s源码

 

;我的assembler程序,EastonWoo procedure

 area init,code,readonly
 import add_8
 import main
 export assembler_add_4
 export assembler_add_5
 export assembler_add_8
 
 entry ;一个进程中至少有一个.
 
start
 ldr sp,=0x33ff8000  ;程序有效开始指令地址:0x30000000,,堆栈设在内存里面,也就是大于0x30000000,注意不要跟代码区冲突;
      ;如果设成0x1000,是写不进栈的,读出来是0xFFFFFFFF.
 
 
 ;********************一:汇编中调用C语言函数:***************************
 stmfd sp!,{r0-r3};start看作子程序,保护好r0-r3 压栈
 
 ;5个实参:优先使用ip,即r0-r3,其中ip.ip作为第5个实参
 ;6个实参:优先使用ip,lr,即r0-r3,ip,lr.其中ip,lr分别作为第5,6个实参.
              ;其中lr会在子程序压栈,出栈.
 ;7个实参:优先使用ip,lr,和一个保存局部变量的寄存器v1,即r0-r3,ip,lr,v1.其中ip,lr,v1分别作为第5,6,7个实参.
              ;其中lr,v1会在子程序压栈,出栈
 ;8个实参:优先使用ip,lr,和二个保存局部变量的寄存器v1,v2,即r0-r3,ip,lr,v1,v2.其中ip,lr,v1,v2分别作为第5,6,7,8个实参.
              ;其中lr,v1,v2会在子程序压栈,出栈
 ;如此类推...
 ;15个参数开始就更复杂了.因为所有的局部变量寄存器全用了.4+2+8=14.以后有时间再来研究.
 
 ;8个参数例子,调用C函数例子.
 mov v2,#8   
 mov v1,#7
 mov lr,#6
 mov ip,#5
 stmfd sp!,{v1-v2} ;压栈,先压8,7,也就释放了v1,v2寄存器.add_8里会用到这些寄存器.
 stmfd sp!,{ip,lr} ;压栈,再压6,5,也就释放了ip,lr寄存器.add_8里会用到这些寄存器.
 mov r3,#4
 mov r2,#3
 mov r1,#2
 mov r0,#1
 bl add_8                ;返回值为r0
 add sp,sp,#16           ;出栈.
 
 
 ;********************二:C语言中调用汇编函数:***************************
 mov v2,#8    ;8个实参进栈.这里就不重复叙述了.
 mov v1,#7
 mov lr,#6
 stmfd sp!,{v1-v2} ;压栈,先压8,7,也就释放了v1,v2寄存器.add_8里会用到这些寄存器.
 stmfd sp!,{ip,lr} ;压栈,再压6,5,也就释放了ip,lr寄存器.add_8里会用到这些寄存器.
 mov r3,#4
 mov r2,#3
 mov r1,#2
 mov r0,#1
 bl main                   ;bl已经保存了b over 这个地址到lr.
 add sp,sp,#16             ;出栈.


 ldmfd sp!,{r0-r3}
 b over


assembler_add_4   ;子程序直接使用r0-r3这四个实参.
 add r0,r0,r1
 add r0,r0,r2
 add r0,r0,r3
 mov pc,lr
 
assembler_add_5   ;子程序直接使用r0-r3这四个实参和ip这个第5个实参.
 add r0,r0,r1
 add r0,r0,r2
 add r0,r0,r3
 add r0,r0,ip
 mov pc,lr
 
;这个8位的汇编函数相当于之前我们讨论的8个参数的被调用C函数的反汇编代码.
assembler_add_8   ;前4个实参,子程序直接使用r0-r3这四个实参;后4个实参分别由ip,lr,v1-v2都是在栈里得来.
 stmfd sp!,{v1-v2,lr}
 add ip,r13,#0x14
 ldmia ip,{v1-v2}
 ldr ip,[r13,#0xc]
 ldr lr,[r13,#0x10]
 add r0,r0,r1
 add r0,r0,r2
 add r0,r0,r3
 add r0,r0,ip
 add r0,r0,lr
 add r0,r0,v1
 add r0,r0,v2
 ldmfd sp!,{v1-v2,pc}
 
over 
 nop   ;无用功.为了编译器不报警
 end

 

2)  func.c 源码,EastonWoo procedure

//我的C语言 实验程序
#include <stdio.h>

 

int add_8(int a,int b,int c, int d, int e, int f,int g, int h)
{
 return (a+b+c+d+e+f+g+h);
 /*
     0x00000000:    e92d4030    0@-.    STMFD    r13!,{r4,r5,r14} ;v1,v2,lr压栈
        0x00000004:    e59d4014    .@..    LDR      r4,[r13,#0x14]   ;v1 = 7
        0x00000008:    e59d5018    .P..    LDR      r5,[r13,#0x18]   ;v2 = 8
        0x0000000c:    e59dc00c    ....    LDR      r12,[r13,#0xc]   ;ip = 5
        0x00000010:    e59de010    ....    LDR      r14,[r13,#0x10]  ;ip = 6
        0x00000014:    e0800001    ....    ADD      r0,r0,r1         ;r0 = 1 + 2 = 3
        0x00000018:    e0800002    ....    ADD      r0,r0,r2         ;r0 = 3 + 3 = 6
        0x0000001c:    e0800003    ....    ADD      r0,r0,r3         ;r0 = 6 + 4 = 0x0A
        0x00000020:    e080000c    ....    ADD      r0,r0,r12        ;r0 = 0x0A + 5 = 0x0F
        0x00000024:    e080000e    ....    ADD      r0,r0,r14        ;r0 = 0x0F + 6 = 0x15
        0x00000028:    e0800004    ....    ADD      r0,r0,r4         ;r0 = 0x15 + 7 = 0x1C
        0x0000002c:    e0800005    ....    ADD      r0,r0,r5         ;r0 = 0x1C + 8 = 0x24
        0x00000030:    e8bd8030    0...    LDMFD    r13!,{r4,r5,pc}  ;把局部寄存器和下一指令出栈,pc 等于 bl add_8的下一条指令地址
 */
}


extern assembler_add_4(int a, int b, int c,int d);
extern assembler_add_5(int a, int b, int c,int d,int e);
extern assembler_add_8(int a,int b,int c, int d, int e, int f,int g, int h);

int main(int a,int b,int c, int d, int e, int f,int g, int h)
{
 int sum1 = 0;
 int sum2 = 0;
 int sum3 = 0;
 
 sum1 = assembler_add_4(a,b,c,d);
 sum2 = assembler_add_5(a,b,c,d,e);
 sum3 = assembler_add_8(a,b,c,d,e,f,g,h);
 return (sum1+sum2+sum3);
 /*
  0x00000034:    e92d4ff0    .O-.    STMFD    r13!,{r4-r11,r14}   ;局部寄存器和lr入栈.运行得sp=0x33ff7fe0,运行后sp=0x33ff7fbc
        0x00000038:    e24dd014    ..M.    SUB      r13,r13,#0x14       ;新开辟5个sp空间,共20字节.运行得sp=0x33ff7fbc,运行后sp=0x33ff7fa8
        0x0000003c:    e59d8038    8...    LDR      r8,[r13,#0x38]      ;r8 = 5;0x33ff7fe0的数据给r8,即进main前的ip
        0x00000040:    e59d9044    D...    LDR      r9,[r13,#0x44]      ;r9 = 8;0x33ff7fec的数据给r3,即进main前的v2
        0x00000044:    e1a07003    .p..    MOV      r7,r3               ;r7 = r3 = 4  ;r0-r3都是全局寄存器,即进main前的r0-r3
        0x00000048:    e1a06002    .`..    MOV      r6,r2               ;r6 = r2 = 3
        0x0000004c:    e1a05001    .P..    MOV      r5,r1               ;r5 = r1 = 2
        0x00000050:    e1a04000    .@..    MOV      r4,r0               ;r4 = r0 = 1
        0x00000054:    e1a03007    .0..    MOV      r3,r7               ;r3 = r7 = 4  ;不知道这有什么用?像是多此一举.其实这是调用assembler_add_4前的4个实参;前面的r7 = r3只是保护现场. 
        0x00000058:    e1a02006    . ..    MOV      r2,r6               ;r2 = r6 = 3
        0x0000005c:    e1a01005    ....    MOV      r1,r5               ;r1 = r5 = 2
        0x00000060:    e1a00004    ....    MOV      r0,r4               ;r0 = r4 = 1
        0x00000064:    ebfffffe    ....    BL       assembler_add_4     ;调用assembler_add_4函数,已把MOV r10,r0指令地址保存到了lr
        0x00000068:    e1a0a000    ....    MOV      r10,r0             ;r10 = r0 = 0x0A;把调用assembler_add_4函数的返回值保存到r10
        0x0000006c:    e1a03007    .0..    MOV      r3,r7               ;r3 = r7 = 4  ;又是一个函数的实参赋值.
        0x00000070:    e1a02006    . ..    MOV      r2,r6               ;r2 = r6 = 3
        0x00000074:    e1a01005    ....    MOV      r1,r5               ;r1 = r5 = 2
        0x00000078:    e1a00004    ....    MOV      r0,r4               ;r0 = r4 = 1
        0x0000007c:    e58d8000    ....    STR      r8,[r13,#0]         ;r8 入栈.即进main前的ip保存到sp地址0x33ff7fa8里.
        0x00000080:    ebfffffe    ....    BL       assembler_add_5     ;调用assembler_add_5函数,已把MOV r11,r0指令地址保存到了lr
        0x00000084:    e1a0b000    ....    MOV      r11,r0              ;r11 = r0 = 0x0F;把调用assembler_add_5函数的返回值保存到r11
        0x00000088:    e59d103c    <...    LDR      r1,[r13,#0x3c]      ;r1 = 6;0x33ff7fe4的数据给r1,即进main前的lr
        0x0000008c:    e59d2040    @ ..    LDR      r2,[r13,#0x40]      ;r2 = 7;0x33ff7fe8的数据给r2,即进main前的v1
        0x00000090:    e98d0206    ....    STMIB    r13,{r1,r2,r9}      ;r1,r2,r9 入栈.即把r1=6,r2=7,r9=8分别放到0x33ff7fac,0x33ff7fb0,0x33ff7fb4里.
        0x00000094:    e58d8000    ....    STR      r8,[r13,#0]         ;r8 入栈.即进main前的ip保存到sp地址0x33ff7fa8里.
        0x00000098:    e1a02006    . ..    MOV      r2,r6               ;r2 = r6 = 3  ;又是一个函数的实参赋值.
        0x0000009c:    e1a01005    ....    MOV      r1,r5               ;r1 = r5 = 2
        0x000000a0:    e1a03007    .0..    MOV      r3,r7               ;r3 = r7 = 4
        0x000000a4:    e1a00004    ....    MOV      r0,r4               ;r0 = r4 = 1
        0x000000a8:    ebfffffe    ....    BL       assembler_add_8     ;调用assembler_add_8函数,已把MOV r1,r0指令地址保存到了lr
        0x000000ac:    e1a01000    ....    MOV      r1,r0               ;r1 = r0 = 0x24;把调用assembler_add_8函数的返回值保存到r1
        0x000000b0:    e08a200b    . ..    ADD      r2,r10,r11          ;r2 = r10 + r11 = 0x0A + 0x0F = 0x19;也就是sum1 + sum2
        0x000000b4:    e0820001    ....    ADD      r0,r2,r1            ;r0 = r2 + r1 = 0x19 + 0x24 = 0x3D;也就是sum1 + sum2 + sum3
        0x000000b8:    e28dd014    ....    ADD      r13,r13,#0x14      ;释放新开辟的栈空间,共20个字节.
        0x000000bc:    e8bd8ff0    ....    LDMFD    r13!,{r4-r11,pc}    ;出栈.恢复局部变量.add sp,sp,#16的地址赋给了pc.
        总的来说,函数里面要做的事分以下5点:
         1),保护现场.主要保护要使用到的局部寄存器和下一条指令lr
         2),开辟栈空间.方便保存临时数据和再次调用子函数的参数入栈.
         3),函数体的逻辑运算.
         4),释放新开辟的栈空间.
         5),恢复现场.出栈.
        这跟我之前写的一篇博文<堆栈在内存中的压栈和弹栈工作原理>原理是差不多的.
 */
}

 
 
2.设置ads工程
    post-linker : ARM fromELF
    ARM Assembler/ARM C Compiler : ARM902T
    ARM Linker -> Output -> RO Base : 0x30000000
               -> Options -> Image entry point : 0x30000000
               -> Layout -> Object/Symbol : call_C_func.o
                         -> Section : init
 
 
3.设置axd调试:
3-1) 设置"启动AXD时自 动初始化SDRAM":
    Options-> Configure Interface-> Session File
                勾上Run Configration Script
                按Browse把脚本文件2440init.txt加进来
脚本2440init.txt:
 
setmem 0x53000000,0x00000000,32
setmem 0x4a000008,0xffffffff,32
setmem 0x4a00001c,0x00007fff,32
setmem 0x48000000,0x2212d110,32
setmem 0x48000004,0x00000f40,32
setmem 0x48000008,0x00002e50,32
setmem 0x4800000c,0x00002e50,32
setmem 0x48000010,0x00002e50,32
setmem 0x48000014,0x00002e50,32
setmem 0x48000018,0x00002e50,32
setmem 0x4800001c,0x00018005,32
setmem 0x48000020,0x00018005,32
setmem 0x48000024,0x00960542,32
setmem 0x48000028,0x00000032,32
setmem 0x4800002c,0x00000030,32
setmem 0x48000030,0x00000030,32
 
其实这个命令的作用就是设置CPU的看门狗,中断等等.
 
3-2) 设置"连接JINK调试库"
    Options-> Configur Target
             按Add 把 Jlink安装目录下的JLinkRDI.dll加进来
             按Configure 生效
             按OK 结束设置.
 
3-3) 重启一下AXD 就可以调试了.

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值