Keil软件“C语言”与“汇编”混编 - 相关知识整理(3)

2019-08-30 16:46

;******** From Disassemble ********

126: ?C_STARTUP: LJMP STARTUP1 127:

128: RSEG ?C_C51STARTUP 129:

130: STARTUP1: ;该段程序把内存清零 131:

132: IF IDATALEN <> 0

C:0x0000 020003 LJMP STARTUP1(C:0003) 133: MOV R0,#IDATALEN - 1 C:0x0003 787F MOV R0,#0x7F 134: CLR A C:0x0005 E4 CLR A

135: IDATALOOP: MOV @R0,A C:0x0006 F6 MOV @R0,A

136: DJNZ R0,IDATALOOP

C:0x0007 D8FD DJNZ R0,IDATALOOP(C:0006)

185: MOV SP,#?STACK-1 ;设制CPU的堆栈起始地址 186:

187: ; This code is required if you use L51_BANK.A51 with Banking Mode 4 188: ; Code Banking

189: ; Select Bank 0 for L51_BANK.A51 Mode 4 190: #if 0

191: ; Initialize bank mechanism to code bank 0 when using L51_BANK.A51 with Banking Mode 4.

192: EXTRN CODE (?B_SWITCH0)

193: CALL ?B_SWITCH0 ; init bank mechanism to code bank 0 194: #endif 195: ;

C:0x0009 758107 MOV SP(0x81),#0x07 196: LJMP ?C_START C:0x000C 02000F LJMP main(C:000F) 2: {

;在实际操作中未找到原文中的下3行信息 ; RSEG ?PR?main?TESTMAIN ;main:

;; void main(void)

C:0x000F 80FE SJMP main(C:000F) ;main()函数 C:0x0011 00 NOP C:0x0012 00 NOP C:0x0013 00 NOP C:0x0014 00 NOP C:0x0015 00 NOP

现在分析上面的汇编程序就会明白c51程序是如何启动的。 该程序有三个代码段;

第一个代码段?C_STARTUP在0x0000地址,是CPU第一条指令的入口,它只有一条长跳转指令,直接跳到第二个代码段.

第二个代码段?C_C51STARTUP是可重定位的段,该程序把内存清零,然后再设置CPU的堆栈,最后跳转到main()函数.

第三个代码段就是main()函数,在keil c51编译器里main()的段地址名就是?C_START。 还有一个IDATA数据段?STACK就是堆栈,?STACK用于设制CPU的堆栈起始地址,这是由keil编译器自动完成的

/*******************************************************************/ 内容二:和函数有关的数值的存储

/*******************************************************************/ keil c51函数的返回值是存储在r0-r7中的。

多字节变量在存储器里都是低地址存高位,高地址存低位。 main()函数的局部变量都是放在存储器里的,不象别的函数先选寄存器r0-r7存放,如果不够用再存入存储器里。 看下面的示例; c51程序;

unsigned int SumXY(unsigned int X,Y); void main(void)

{unsigned int a,b,c; a=0x5500;

b=0xaa; while (1) {

c=SumXY(a,b); } }

unsigned int SumXY(unsigned int X,Y) {unsigned int Z; Z=X+Y; return Z; }

编译后的反汇编代码列表;

C:0x0000 020027 LJMP STARTUP1(C:0027)

4: void main(void)

5: {unsigned int a,b,c; 6: a=0x5500;

C:0x0003 750855 MOV 0x08,#0x55 ;ram地址0x08和0x09存放变量a=0x5500。 C:0x0006 750900 MOV 0x09,#0x00 7: b=0xaa;

C:0x0009 750A00 MOV 0x0A,#0x00 ;ram地址0x0A和0x0B存放变量b=0x00AA。 C:0x000C 750BAA MOV 0x0B,#0xAA

8: while (1) 9: {

10: c=SumXY(a,b);

C:0x000F AD0B MOV R5,0x0B ;寄存器R4和R5传递变量a的值。 C:0x0011 AC0A MOV R4,0x0A

C:0x0013 AF09 MOV R7,0x09 ;寄存器R6和R7传递变量b的值。 C:0x0015 AE08 MOV R6,0x08

C:0x0017 120020 LCALL SumXY(C:0020) ;调用函数SumXY(a,b)求c=a+b

C:0x001A 8E0C MOV 0x0C,R6 ;函数SumXY(a,b)返回的整型值存在R6和R7里,

C:0x001C 8F0D MOV 0x0D,R7 ;把返回值存入变量c,ram地址0x0C和0x0D存放变量c 11: } 12: } 13:

C:0x001E 80EF SJMP C:000F

14: unsigned int SumXY(unsigned int X,Y) 15: {unsigned int Z; 16: Z=X+Y;

C:0x0020 EF MOV A,R7 ;参数变量X放在寄存器R6和R7里 C:0x0021 2D ADD A,R5 ;参数变量Y放在寄存器R4和R5里 C:0x0022 FF MOV R7,A C:0x0023 EE MOV A,R6

C:0x0024 3C ADDC A,R4 ;计算Z=X+Y;

C:0x0025 FE MOV R6,A ;局部变量Z也放在寄存器R6和R7里 17: return Z; ;由寄存器R6和R7里返回函数的值 C:0x0026 22 RET

151: MOV SP,#?STACK-1

152: ; This code is required if you use L51_BANK.A51 with Banking Mode 4 153: ; EXTRN CODE (?B_SWITCH0)

154: ; CALL ?B_SWITCH0 ; init bank mechanism to code bank 0 C:0x0027 75810D MOV SP(0x81),#0x0D 155: LJMP ?C_START C:0x002A 020003 LJMP main(C:0003)

/*******************************************************************/ 内容三:函数的入口地址,如何调用汇编函数,c和汇编的混合编程

/*******************************************************************/ c函数的函数名是一个指向函数的指针常量,它的值就是函数的入口地址。 从汇编程序上看,函数名也是该汇编函数代码段的入口地址标号。

调用汇编函数就是调用汇编函数的入口地址标号,但是要注意c函数名和汇编函数标号之间的转换规则。 1. 不带参数的汇编函数标号和c函数名相同.

2. 带参数的汇编函数标号在c函数名前加字符_,例如;如果c函数名是SumXY,汇编函数标号是_SumXY 3. 再入函数的汇编函数标号在c函数名前加_?,例如;如果c函数名是DoTask,汇编函数标号是_?DoTask 程序示例,该例有两个文件,一个文件是exam1.c,一个文件是funcasm.c 主程序文件: exam1.c

//声明外部汇编语言函数,和声明c函数方法相同。

extern unsigned int SumXY(unsigned int X,Y); extern void Delay(unsigned char T);

void main(void)

{unsigned int a,b,c; a=0x5500; b=0x00aa; while (1) {

Delay(100); c=SumXY(a,b); } }

混合编程文件: funcasm.c

//c和汇编的混合编程演示.

//注意要把汇编语言函数放在文件前面。

//求Z=X+Y的汇编语言函数,Z,X,Y是整型数。 #pragma ASM

PUBLIC _SumXY

?PR?_SumXY?FUNCASM SEGMENT CODE RSEG ?PR?_SumXY?FUNCASM _SumXY: ;求Z=X+Y

MOV A,R7 ;参数X放在寄存器R6和R7里 ADD A,R5 ;参数Y放在寄存器R4和R5里 MOV R7,A MOV A,R6

ADDC A,R4 ;扑鉠=X+Y;

MOV R6,A ;局部变量Z也放在寄存器R6和R7里 RET

#pragma ENDASM

//c语言函数,延时函数。

void Delay(unsigned char T) {unsigned char i;

for (i=0;i

for (i=0;i

上面程序编译后的反汇编代码列表;

C_STARTUP:

C:0x0000 020041 LJMP STARTUP1(C:0041)

main:

//给变量a和b赋值 a=0x5500; b=0x00aa; C:0x0003 750855 MOV 0x08,#0x55 C:0x0006 750900 MOV 0x09,#0x00 C:0x0009 750A00 MOV 0x0A,#0x00 C:0x000C 750BAA MOV 0x0B,#0xAA

//调用延时函数Delay(100);

C:0x000F 7F64 MOV R7,#0x64

C:0x0011 120025 LCALL DELAY(C:0025)

//求c=SumXY(a,b);

C:0x0014 AD0B MOV R5,0x0B C:0x0016 AC0A MOV R4,0x0A C:0x0018 AF09 MOV R7,0x09

C:0x001A AE08 MOV R6,0x08

C:0x001C 12003A LCALL SUMXY(C:003A) ;调用汇编语言函数SumXY(unsigned int X,Y) C:0x001F 8E0C MOV 0x0C,R6 C:0x0021 8F0D MOV 0x0D,R7 C:0x0023 80EA SJMP C:000F

//c语言延时函数的反汇编代码

//void Delay(unsigned char T) DELAY:

C:0x0025 E4 CLR A C:0x0026 FE MOV R6,A C0001:

C:0x0027 EE MOV A,R6 C:0x0028 C3 CLR C C:0x0029 9F SUBB A,R7

C:0x002A 500D JNC C0007(C:0039) C:0x002C E4 CLR A C:0x002D FE MOV R6,A C0004:

C:0x002E EE MOV A,R6 C:0x002F C3 CLR C C:0x0030 9F SUBB A,R7

C:0x0031 5003 JNC C0003(C:0036) C:0x0033 0E INC R6

C:0x0034 80F8 SJMP C0004(C:002E) C0003:

C:0x0036 0E INC R6

C:0x0037 80EE SJMP C0001(C:0027) C0007:

C:0x0039 22 RET

//汇编语言函数SumXY(unsigned int X,Y)的反汇编代码,求Z=X+Y SUMXY:

C:0x003A EF MOV A,R7 C:0x003B 2D ADD A,R5 C:0x003C FF MOV R7,A C:0x003D EE MOV A,R6 C:0x003E 3C ADDC A,R4 C:0x003F FE MOV R6,A C:0x0040 22 RET

//程序启动代码;

STARTUP1:

C:0x0041 75810D MOV SP(0x81),#0x0D C:0x0044 020003 LJMP main(C:0003)

参数传递规则例子:

fun1(int a)//a是第一个参数,在R6,R7中传递.

fun2(int b ,int c , int *d)//b在R6,R7中传递;C在R4,R5中传递;d在 R1,R2,R3中传递. fun3(long e ,long f)//e在R4-R7中传递;f不能在寄存器中传递,只能在参数传递段中传递. fun3(float g ,char h)//g在R4-R7中传递;h不能在寄存器中传递,只能在参数传递段中传递

C语言和汇编语言的变量以及函数的接口问题

在C和汇编混合编程的时候,存在C语言和汇编语言的变量以及函数的接口问题。

在C程序中定义的变量,编译为.asm文件后,都被放进了.bss区,而且变量名的前面都带了一个下划线。在C程序中定义的函数,编译后在函数名前也带了一个下划线。例如: extern int num 就会变成 .bss _num, 1 extern float nums[5] 就会变成 .bss _nums, 5 extern void func ( ) 就会变成 _func, 一、汇编和C的相互调用可以分以下几种情况: (1)汇编程序中访问c程序中的变量和函数。

在汇编程序中,用_XX就可以访问C中的变量XX了。访问数组时,可以用_XX+偏移量来访问,如_XX+3访问了数

组中的XX[3]。

在汇编程序调用C函数时,如果没有参数传递,直接用_funcname 就可以了。如果有参数传递, 则函数中最左边的一个参数由寄存器A给出,其他的参数按顺序由堆栈给出。返回值是返回到A寄存器或者由A寄存器给出的地址。同时注意,为了能够让汇编语言 能访问到C语言中定义的变量和函数,他们必须声明为外部变量,即加extern 前缀。

(2)c程序中访问汇编程序中的变量

如果需要在c程序中访问汇编程序中的变量,则汇编程序中的变量名必须以下划线为首字符,并用global使之成为全局变量。

如果需要在c程序中调用汇编程序中的过程,则过程名必须以下划线为首字符,并且,要根据c程序编译时使用的模式是stack-based model还是register argument model来正确地编写该过程,使之能正确地取得调用参数。 (3)在线汇编

在C程序中直接插入 asm(“ *** ”),内嵌汇编语句,需要注意的是这种用法要慎用,在线汇编提供了能直接读写硬件的能力,如读写中断控制允许寄存器等,但编译器并不检查和分析在线汇编语言,插入在线汇编语言改变汇编环境或可能改变C变量的值可能导致严重的错误。 二、汇编和C接口中寻址方式的改变:

需要注意的是,在C语言中,对于局部变量的建立和访问,是通过堆栈实现的,它的寻址是通过堆栈寄存器SP实现的。而在汇编语言中,为了使程序代码变得更为精简,TI在直接寻址方式中,地址的低7位直接包含在指令中,这低7位所能寻址的具体位置可由DP寄存器或SP寄存器决定。具体实现可通过设置ST1寄存器的CPL位实现,CPL=0,DP寻址,CPL=1,SP寻址。在DP寻址的时候,由DP提供高9位地址,与低7位组成16位地址;在SP寻址的时候, 16位地址是由SP(16位)与低7位直接相加得来。

由于在C语言的环境下,局部变量的寻址必须通过SP寄存器实现,在混合编程的时候,为了使汇编语言不影响堆栈寄存器SP,通常的方式是在汇编环境中使用DP方式寻址,这样可以使二者互不干扰。编程中只要注意对CPL位正确设置即可。

汇编与C语言混合编程的关键问题

1、 C程序变量与汇编程序变量的共用

为了使程序更易于接口和维护,可以在汇编程序中引用与C程序共享的变量: .ref_to_dce_num,_to-dte_num,_to_dce_buff,_to_dte_buff 在汇编程序中引用而在C程序可直接定义的变量:

unsigned char to_dte_buff[BUFF_SIZE]; //DSP发向PC机的数据

int to_dte_num; //缓冲区中存放的有效字节数 int to_dte_store; //缓冲区的存放指针 int to_dte_read; //缓冲区的读取指针 这样经过链接就可以完成对应。 2、 程序入口问题

在C程序中,程序的入口是main()函数。而在汇编程序中其入口由*.cmd文件中的命令决定,如:-e main_start;程序入口地址为 main_start。这样,混合汇编出来的程序得不到正确结果。因为C到ASM的汇编有默认的入口c-int00,从这开始的一段程序为C程序的运行做准备工作。这些工作包括初始化变量、设置栈指针等,相当于系统壳不能跨越。这时可在*.cmd文件中去掉语句:-e main_start。如仍想执行某些汇编程序,可以C函数的形式执行,如:

main_start(); //其中含有其他汇编程序

但前提是在汇编程序中把_main_start作为首地址,程序以rete结尾(作为可调用的函数)的程序段,并在汇编程序中引用_main_start,即.ref _main_start。 3、 移位问题

在C语言中把变量设为char型时,它是8位的,但在DSP汇编中此变量仍被作为16位处理。所以会出现在C程序中的移位结果与汇编程序移位

结果不同的问题。解决的办法是在C程序中,把移位结果再用0X00FF去“与”一下即可。 4、 堆栈问题

在汇编程序中对堆栈的依赖很小,但在C程序中分配局部变量、变量初始化、传递函数变量、保存函数返回地址、保护临时结果功能都是靠堆栈完成。而C编译器无法检查程序运行时堆栈能否溢出。 5、 程序跑飞问题

编译后的C程序跑飞一般是对不存在的存储区访问造成的。首先要查.MAP文件与memory map图对比,看是


Keil软件“C语言”与“汇编”混编 - 相关知识整理(3).doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: