arm第十一天(汇编与C混调、异常处理)

汇编程序与C程序混合调用

  • 在C语言中如何调用汇编语言实现的函数
  • 在C语言中如何使用汇编语言定义的变量
  • 汇编语言中如何调用C语言的函数
  • 汇编语言中如何调用C语言定义的变量

  • 在C语言中如何调用汇编语言实现的函数
    例如用汇编比较两个字符串是否相等的函数的代码片段:

    .text
    .code32
    .global my_strcmp
my_strcmp:
    ldrb r2,[r1],#1   //"ledon" 首地址
    ldrb r3,[r0],#1   //buf从键盘输入的命令字符
    cmp  r2,#0
    beq cmp_end
    .....  
cmp_end: 
    sub r0,r2,r3
    bx lr

C语言调用函数的过程的代码片段:
main.c

//要在C代码中得到声明适合于C函数原型
extern void my_strcmp(char *d,const char*s);

int main(void)
{
    char buf[256];
    uart0_gets(buf);//获取键盘输入的命令
    my_strcmp(buf,"ledon");//汇编实现的函数
    //传参数默认传递  r0,r1,r2,r3,...    这里有两个参数,因此为r0,r1
    //汇编使用寄存器的时候,如果要混调,要按顺序使用

}

遵循的原则:

  1. 汇编函数的名字声明为全局标号 “.global my_strcmp”
  2. C程序中如要调用这个汇编函数,需要声明该函数的函数原型
  extern void my_strcmp(char *d,const char*s);
  1. 按照C语言的方式去调用该函数

C语言中如何使用汇编语言定义的变量

     .data
     .global yy
  yy:
     .word 0x100  //定义了一个整形变量,变量名称为yy

1,在汇编中将变量声明为全局变量
2,在C语言中声明该变量为外部变量

extern int yy;

3,按照C的方式去使用该变量

汇编语言中如何调用C语言的函数

int add(int a,int b,int c,int d){
   return (a+b+c+d);
}

1,在C程序中该函数要定义为全局,即函数前面不能加static
2,汇编程序中将该函数声明为外部标号

       .extern add //伪操作,意味着该函数在C中实现

3,按照汇编的方式调用

//准备参数,参数不能随便放,要按照顺序
mov r0,#0   
mov r1,#1
mov r2,#2
mov r3,#3
//调用
bl add
cmp r0,#0 //C函数的时候,将返回值放在了r0

关键是:参数的传递规则

int add(int a,int b,int c,int d)
r0, r1 r2 r3

汇编语言中如何调用C语言定义的变量
int a ;
1,C中声明为全局变量
2,汇编程序中声明该变量为外部变量

    .extern a;

3,按照汇编的方式使用

ldr r0,=a   //a的地址
ldr r1,[r0]

总结:
被调用方循环的原则:
将被调用的函数或变量都要定义为全局
调用方:
声明被调用函数的函数原型或者变量为外部标号
参数传递:
r0,r1,r2,r3
r0作为返回值

案例:
在C程序中调用汇编实现的函数
将用汇编实现的字符串比较函数替换掉用C实现的字符串比较函数
1,汇编函数(字符串比较函数)定义为全局标号
2,在C程序中将该汇编函数声明函数原型
3,替换掉C的字符吕比较函数,用汇编函数

astrcmp.s

     .text
     .code 32
     .global a_strmcmp
a_strmcmp:
     @不用获取地址,因为C函数调用时,会通过r0,r1将比较的字符串的地址传递过来
     @r0 str1 对应键盘输入的命令
     @r1 str2 定义好的命令的名字
     @r2 r0,r3 r1
     @比较
     @返回,通过r0返回;通过r0保存比较的结果
     @     当r00,相同
     @     当r00,不同
     @bx lr 返回
 cmp_loop://循环比较
     ldrb r2,[r0],#1
     ldrb r3,[r1],#1
     cmp r2,#0
     beq cmp_end
     cmp r2,r3
     beq cmp_loop

 cmp_end:    @返回
     sub r0,r2,r3
     bx lr 
     .end 

.h声明函数原型

  //a_strmcmp是汇编实现的字符串比较函数
  //返回值为0表示比较成功,非0比较失败
  extern int a_strmcmp(const char *str1,const char *str2);

修改makefile
在C中调用

start.s

    .text
    .code 32
    .gloal start
    @调用C实现的main
    .extern main
    .extern __bss_start  @  r0 bss的起始地址
    .extern __end

start:
    stmfd sp!,{lr}  @lr入栈
    ldr r0,=__bss_start
    ldr r1,__end
    mov r2,#0
clear_bss:    @清除bss段
    str,r2,[r0],#4     @字的存储指令,清完一个字,+4表示字对齐
    cmp r0,r1   @循环清0
    bcc clear_bss

    bl main @跳转到C语言中的main中执行
    ldmfd sp!,{pc}  @出栈
    .end

异常处理
1,复位异常,进入管理SVC模式
2,未定义指令异常,不能识别指令,进入未定义模式
3,软中断异常,指行swi指令,进入SVC模式
4,预取指令异常,没有取到指定的指令,进入中止模式
5,数据中止异常,没有取到指定的数据,进入中止模式
6,中断,进入中断模式
7,快速中断,进入快速中断模式

工作模式:
异常模式
管理
快速中断
中断
中止

非异常模式

当发生异常时,如保处理?
CPU做:
1,拷贝CPSR到相应异常模式的CPSR,SPSR_mode = CPSR (备份)
2,设置适当的CPSR的位:
1. 将处理器的状态改为ARM状态,CPSR的bit[5] = 0
2. 改变处理器的工作模式,进入到对应的异常工作模式,改CPSR的bit[4:0]到对应的异常模式
3. 设置中断禁止位,
3,保存返回地址到LR_mode = PC -4(PC -2),异常返回的问题
4,设置PC为相应异常处理程序的入口(异常向量表)
5,处理异常,执行异常处理
6,异常返回,CPSR = SPSR_mode (MOVS PC ,LR)

异常向量表:
这里写图片描述

软中断
这里写图片描述

系统执行代码,执行到SWI 0x01指令,在该指令的执行阶段,发生软件中断异常
CPU:
1,备份CSPR,SPSR_SVC = CPSR
2,修改CPSR
1.改状态,ARM状态,CPSR的bit[i]=0
2,改模式,SVC管理模式,CPSR的bit[4:0] = 10011
3. 改中断禁止位
3,保存返加地趣为LR_SVC
4,修改PC为异常向量表中对应的入口地址,PC = 0x8
对应指令:ldr pc,swi_hdl
再次修改PC = swi_hdl函数地址
5,swi_hdl,软中断异常处理程序 ,在此程序中调用C语言缩写的较为复杂的处理程序 (汇编调用C函数的混合调用)
6,当C函数处理异常完毕之后,返回汇编swi_hdl,再做最后的异常返回.
7,movs pc,ls CPSR = SPSR_SVC
8,接下来继续执行swi指令的下一条指令

     .text
     .code 32
     .global vector_start
     .extern reset
vector_start:    @异常向量表
      b reset
      ldr pc,_und_hdl
      ldr pc,_swi_hdl
      ldr pc,_pabt_hdl
      ldr pc,dabt_hdl
      b  .
      ldr pc,irq_hdl
      ldr pc,fiq_hdl

und_hdl:
      .word _und_handler
_swi_hdl:
      .word _swi_handler
...

_swi_handler:
      stmfd sp!,{r0-r12,lr} @r0r12入栈
      bl c_swi_handler   @软中断处理函数,C函数 
      ldmfd sp!,{r0-r12,pc}^    @出栈
      .global swi_test1 
swi_test1: 
      stmfd sp!,{lr}    
      stmfd sp!,{pc}       

reset.s

     .text
     .global vector_start
     .extern reset
reset:    
     msr cpsr_c,#0xd3
     ldr sp,0xd0036000
     .....

reset.s
1,将模式改为SVC管理模式,并且屏蔽中断,初始化栈顶指针SP
2,安装MMU的地址转换表,之后CPU给的地址都是虚拟地址
3,初始化IRQ和FIQ模式下栈顶指针
4,清空BSS段
5,跳转到main执行主函数(shell,汇编调用C的混合调用)

提取swi指令中中断号:

swi指令格式:
1,在ARM状态下,执行ARM指令,SWI指令,低24bit为中断号
2,在thumb状态下,执行thumb指令,swi指令,低8bit为中断号

ARM指令格式(32bit):
这里写图片描述

Thumb指令格式:

这里写图片描述

1,确定执行swi指令时,CPU所片的状态,ARM? thumb?
由于发生异常时,SPSR_SVC = CPSR
判断SPSR的bit[5] = 0,ARM状态,swi指令是指令(32)
bit[5] = 1,thumb状态,swi指令是thumb指令(16)
1,读SPSR寄存器到R0,MRS指令
2,用位运算bit[5] ANDS EQ(ARM) NE(thumb)
2,获取swi指令的编码
由于发生异常时,保存返回地址 lr = pc -4 ARM
= pc - 2 thumb
ARM 状态: swi指令码的地址,lr - 4
Thumb状态,swi指令码的地址,lr-2

 ldreq  加载ARM指令    r0,[lr-4]
 ldrneh 加载thumb指令  r0,[lr - 2]

3,使用位运算获取到swi指令码中中断号,将中断号存在R0
ARM指令:提取低24bit
Thumb:提取低8bit
通过位运算,中断号保存在r0
1,biceq r0 24bit , 清除24bit
2,andne r0 8bit,
4,R0中断号做为参数,传递给C函数
5,进入c_swi_handler(unsigned int num),伪代码如下:

    c_swi_handler(unsigned int num){
        switch(num){
           case 1:
              ...
              break;
           case 2:
              ....
              break;
           ...
        }
    }

取指,解码,执行,在swit指令的第三个阶段,执行阶段,发生异常
1,SPSR= CPSR
2,改CSPR ,ARM状态,SVC模式,中断禁止位
3,保存返回地址 LR= PC -4 add指令地址
= PC -2

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值