汇编 C混调 异常处理

来源:互联网 发布:指尖软件 编辑:程序博客网 时间:2024/05/21 17:20

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

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

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

    .text    .code32    .global my_strcmpmy_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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

遵循的原则:

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

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

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

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

extern int yy;
  • 1
  • 1

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

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

int add(int a,int b,int c,int d){   return (a+b+c+d);}
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

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

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

3,按照汇编的方式调用

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

关键是:参数的传递规则

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

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

    .extern a;
  • 1
  • 1

3,按照汇编的方式使用

ldr r0,=a   //a的地址ldr r1,[r0]
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

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

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

astrcmp.s

     .text     .code 32     .global a_strmcmpa_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 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

.h声明函数原型

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

修改makefile 
在C中调用

start.s

    .text    .code 32    .gloal start    @调用C实现的main    .extern main    .extern __bss_start  @  r0 bss的起始地址    .extern __endstart:    stmfd sp!,{lr}  @lr入栈    ldr r0,=__bss_start    ldr r1,__end    mov r2,#0clear_bss:    @清除bss段    str,r2,[r0],#4     @字的存储指令,清完一个字,+4表示字对齐    cmp r0,r1   @循环清0    bcc clear_bss    bl main @跳转到C语言中的main中执行    ldmfd sp!,{pc}  @出栈    .end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

异常处理 
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 resetvector_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_hdlund_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}       
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

reset.s

     .text     .global vector_start     .extern resetreset:         msr cpsr_c,#0xd3     ldr sp,0xd0036000     .....
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

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;           ...        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

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

0 0