Keil软件“C语言”与“汇编”混编 —— 相关知识整理.pdf
Keil 软件软件C 语言语言与与汇编汇编混编混编 相关知识整理相关知识整理 用 Keil 在 C 中嵌入汇编.1 在 Keil 中嵌入汇编2 介绍直接嵌入汇编代码的方法4 采用汇编可能会有的好处 5 Keil C 语言与汇编语言混合编程.7 深入剖析 Keil C51 从汇编到 C51.9 C 语言和汇编语言的变量以及函数的接口问题 14 汇编与 C 语言混合编程的关键问题. 15 KEIL 段重定位 16 用用 Keil 在在 C 中嵌入汇编中嵌入汇编 早前公布了 C 和汇编混编的温度控制器程序,收到一些朋友的询问,他们无法在自己程序中使用我的 18B20 的汇 编子程序或无法正常通过混编后的程序编译。 其实在 KEIL 中嵌入汇编的方法很简单。 如图一, 在 C 文件中要嵌入汇编的地方用pragma asm 和pragma endasm 分隔开来,这样编译时 KEIL 就知道这中间的一段是汇编了。 图 1 在有加入汇编的文件中,还要设置编译该文件时的选项 图 2 Generate Assembler SRC File 生成汇编 SRC 文件 Assemble SRC File 封装汇编文件 (如图三的状态为选中) 选上这两项就可以在 C 中嵌人汇编了,设置后在文件图示中多了三个红色的小方块。 图 3 为了能对汇编进行封装还要在项目中加入相应的封装库文件, 在笔者的项目中编译模式是小模式所以选用 C51S.LIB。这也是最常用的。这些库文件是中 KEIL 安装目录下的 LIB 目录中。 加好后就可以顺利编译了。(注 我只在 7.0 以上版本使用过) 图 4 在在 Keil 中嵌入汇编中嵌入汇编 1、其实在 KEIL 中嵌入汇编的方法很简单。如图 1,在 C 文件中要嵌入汇编的地方用pragma as m 和pragma endasm 分隔开来,这样编译时 KEIL 就知道这中间的一段是汇编了。 2、在有加入汇编的文件中,还要设置编译该文件时的选项,如图 2 所示。 3、Generate Assembler SRC File 生成汇编 SRC 文件 Assemble SRC File封装汇编文件 (如图 3 的状态为选中) 选上这两项就可以在 C 中嵌人汇编了,设置后在文件图示中多了三个红色的小方块。 4、为了能对汇编进行封装还要在项目中加入相应的封装库文件, 在笔者的项目中编译模式是小 模式所以选用 C51S.LIB。这也是最常用的。这些库文件是中 KEIL 安装目录下的 LIB 目录中。 加好 后就可以顺利编译了 介绍直接嵌入汇编代码的方法介绍直接嵌入汇编代码的方法 1、在 C 文件中要嵌入汇编代码片以如下方式加入汇编代码 pragma ASM ;Assembler Code Here pragma ENDASM 2、在 Project 窗口中包含汇编代码的 C 文件上右键,选择“Options for .”,点击右边的“GenerateAssembler SRC File”和“Assemble SRC File”,使检查框由灰色变成黑色有效状态; 3、根据选择的编译模式,把相应的库文件如 Small 模式时,是 KeilC51LibC51S.Lib加入工程中, 该文件必须作 为工程的最后文件; 采用汇编可能会有的好处采用汇编可能会有的好处 C51 编译器能对 C 语言程序进行高效率的编译,生成高效简洁的代码,在大多数的应用场合,采用 C 语言编 程即可完成预期的任务,但是,在有些场合还是会用到汇编,例如在下面的几种情况下,采用汇编可能会有很多好 处 1、已有程序的移植在单片机领域工作很久的工程人员可能会保留有很多的早期用汇编语言编制的程序模块, 并且这些模块已经经过实际应用的验证,如果重新用 C 编程,可能工作量很大,这时就可以用嵌入汇编的方式把以 前的汇编模块植入新的应用,可以明显的加快开发的进度。 2、局部功能需要足够短的执行时间在有些应用中,部分的功能模块需要有很高的执行效率,而有些汇编的 指令在 C 中没有对应的指令,这给我们对单片机的高效操作带来困难,嵌入汇编可是我们的程序执行更有效率。 3、对一些特定地址进行操作在 C 中我们要对特定地址进行读写,一般用以下两种方式用_AT_指令定义变 量; 定义指向外部端口或数据地址的指针; 在汇编中只需要使用 MOVX A,DPTR 或 MOVX DPTR,A 就可以了, 这样可以增强程序的可读性。 4、其他的需要汇编的应用在这里我们不可能举出所有可能要用汇编的例子,在你的应用中,你可能在一个 或多个应用中感到 C 语言的不足,而需要用到汇编指令,请你记住,可以在 C 中嵌入汇编子程序,这对你的程序 非常有用。 首先介绍一下调用汇编的参数传递规则,见下表 传递的参数char、1 字节指针int、2 字节指针long、float一般指针 第一个参数R7R6,R7R4R7R1,R2,R3 第二个参数R5R4,R5R1,R2,R3 第三个参数R3R2,R3无R1,R2,R3 在下面的例子中我们首先给出一个 C 程序调用汇编的例子,先说明一下,在这个例子中,你完全可以用 C 完 成,我用这个例子,只是为了说明嵌入汇编的方法。 例 1 下面是下面是 C 语言的主程序语言的主程序 include include extern char asmchar c,char b; bit VAL; void main void char out0 x49; Char direct; Char key; SCON 0 x50;/* SCON mode 1, 8-bit UART, enable rcvr */ 10 位异步接收器,可变。REN1,允许接收数据 TMOD | 0 x20;/* TMOD timer 1, mode 2, 8-bit reload */ 定时器 1 工作于模式 2 TH1 0 xfd;/* TH1 reload value for 9600 baud 11.0592MHz */ TR1 1;/* TR1 timer 1 run */ TI 1;/* TI set TI to send first char of UART */ VAL 0; while 1 keygetchar; ifkeyR direct0X01; outasmout,direct;/*汇编子程序调用*/ printf “Right rotaten“; ifkeyL direct0X02; outasmout,direct;/*汇编子程序调用*/ printf “Left rotaten“; printf“bxn“,out; 下面是汇编的子程序(文件名称下面是汇编的子程序(文件名称 asmtest.asm NAME ASM PR_asmASMTEST SEGMENT CODE BI_asmASMTEST SEGMENT BIT PUBLIC _asm BIT PUBLIC _asm RSEG BI_asmASMTEST _asmBIT VALDBIT 1 RSEG PR_asmASMTEST _asmMOV A,R7 MOV C,VAL DJNZ R5,JP1 RRC A JP1 DJNZ R5,JP2 RLC A JP2 MOV 90h,A MOV R7,A MOV VAL,C RET END 现在对这个例子简短的说明,这个例子是用来驱动一个不带细分的三相步进电机,用的是循环移位的指令来实 现的,由于一个字节是 8 位,如果算上进位位,共 9 位,通过付值,可以得到这样的数 100100100,大家可以看 到任意取中间的 3 位,相邻的每 3 位与它都一样,这样我们就可以去中间的 3 位输出到端口去驱动一个三相的步进 电机,通过对这个 9 位的二进制数进行循环移位,可以实现电机的步进,在 51 系列单片机中有两条指令可以帮助 我们进行循环移位操作,这就是 RRC A 和 RLC A 指令,我们只要把第九位保存好,在需要移位操作时,把它付 给 PSW 中 C,再执行循环移位就可以了。 在这个例子中,我发现了几个需要注意的地方 1、在 C 程序中,我不能把 VAL 变量设为 extern 类型,否则在连接时会有警告,导致数值不能传递; 2、在汇编模块中,不能把 SEGMENT BIT 段置为 OVERLAYABLE 即可覆盖段,如果置为可覆盖段,那么在 进入汇编模块时 VAL 变量值丢失; 3、如果把 VAL 变量作为函数的参数传递,出现在返回后在执行 printf 函数后变量值丢失; 上面是本人写的一个 C 程序调用汇编的小例子,在本例中的几个 printf 函数是为了便于在 Keil C51 中模拟调 试时观察运行结果的,在实际应用中可以将这几条去掉,这只是一个简短的示例,目的在于介绍一下 C 调用汇编的 用法,希望对大家有帮助,同时由于本人水平有限,在程序中有些地方可能仍有不周到之处,欢迎广大单片机高手 不吝指出。 Keil C 语言与汇编语言混合编程语言与汇编语言混合编程 C51C51和汇编混合编程和汇编混合编程11C C 语言中嵌入汇编语言中嵌入汇编 1、在 C 文件中要嵌入汇编代码片以如下方式加入汇编代码 pragma ASM ;Assembler Code Here pragma ENDASM 2、在 Project 窗口中包含汇编代码的 C 文件上右键,选择“Options for .” ,点击右边的“Generate Assembler SRC File”和“Assemble SRC File” ,使检查框由灰色变成黑色有效状态; 3、根据选择的编译模式,把相应的库文件如 Small 模式时,是 KeilC51LibC51S.Lib加入工程中, 该文件必 须作为工程的最后文件; 4、这点也是本人要重要说明的即一定要将 ckeilC51下的 STARTUP.A51文件加入项目 5、编译,即可生成目标代码。 来个实例吧 include void mainvoid P21; pragma asm MOV R7,10 DEL MOV R6,20 DJNZ R6, DJNZ R7,DEL pragma endasm P20; C51C51和汇编混合编程和汇编混合编程22无参数传递的函数调用无参数传递的函数调用 先来个例子其中 example.c 和 example.a51为项目中的两个文件 *example.c* extern void delay100; main delay100; *example.a51* PRDELAY100 SEGMENT CODE; 在程序存储区中定义段 PUBLIC DELAY100; 声明函数 RSEG PRDELAY100; 函数可被连接器放置在任何地方 DELAY100 MOV R7,10 DEL MOV R6,20 DJNZ R6, DJNZ R7,DEL RET END 在 example.c 文件中 先声明外部函数,然后直接在 main 中调用即可。 在 example.a51中 PRDELAY100 SEGMENT CODE;作用是在程序存储区中定义段,DELAY100为段名,PR表示段位于程序存储区内 PUBLIC DELAY100;作用是声明函数为公共函数 RSEG PRDELAY100;表示函数可被连接器放置在任何地方,RSEG 是段名的属性,段名的开头为 PR,是 为了和 C51内部命名转换兼容 段名命名转换规律如下 CODEPR XDATAXD DATADT BITBI PDATAPD C51C51和汇编混合编程和汇编混合编程33有参数传递的函数调用有参数传递的函数调用 在写这片文章之前, 写了个试验程序, 但总是通不过, 查看汇编代码发现 c 文件中的语句根本没有被编译进去, 怎么也找不到原因,郁闷 最后在网上搜了个试验程序,把我的程序复制过去,可以编译成功,奇怪了,在我的 project 里就是不行,我 注意到我的 project 编译后出现一条 WARNING * WARNING L7 MODULE NAME NOT UNIQUE MODULE8.obj 8 而同样的程序代码在另外一个 project 中没有 WARNING,肯定是这条 WARNING 语句导致的,里面提到 NAME,难 道和名字有关,马上把 A51文件改个名字(原来 c 文件和 a51文件名字一样) ,编译,哈哈,WARNING 不见了,查看 汇编代码,一切按预想的进行,唉,一个名字害得我不浅啊,记住哦,c 文件和 A51文件不能使用同一个文件名, 不过我还不知道为什么会这样,有高手知道得话请告知,还是进行今天的作业吧 今天说说带参数传递的函数调用,在 C51和汇编之间传递参数的方式有两种,一种是通过寄存器传递参数,C51 中不同类型的实参会存入相应的寄存器,在汇编中只需对相应寄存器进行操作,即达到传递参数的目的。 不同类型的数据及其传递参数的寄存器如下表所示 参数类型charintlong/float通用指针 第1个R7R6 main DELAY10,1000; while1; DELAY.A51 PR_DELAYDELAYSEGMENT CODE PUBLIC _DELAY RSEG PR_DELAYDELAY _DELAY DJNZ R4, DJNZ R5, DJNZ R7, RET END 还要说的是,函数名前要加下划线,表示是有参数传递的函数调用 C51C51和汇编混合编程和汇编混合编程44函数的返回值传递参数函数的返回值传递参数 函数返回值所用的寄存器 返回值类型寄存器说明 BitC由具体标志位返回 char/unsigned char / 1 byte 指针R7 int/unsigned int / 2 byte 指针R6 main example80; pragma asm DJNZ R7, DJNZ R6, pragma endasm while1; 函数返回值在 R6,R7中。 深入剖析深入剖析 Keil C51 从汇编到从汇编到 C51 /*/ 内容一内容一mainmain函数和启动代码函数和启动代码 /*/ 汇编是从 org 0000h 开始启动,那么 keil c51是如何启动 main函数的 keil c51有一个启动程序 startup.a51, 它总是和 c 程序一起编译和链接。 下面看看它和 main函数是如何编译的; 主函数如下; void mainvoid 这是个无条件空循环。 while1 把上面的 main函数编译后的汇编程序和反汇编代码整理后对照如下; ; From STARTUP.A51 NOMOD51 IDATALENEQU80H XDATASTARTEQU0 XDATALENEQU0 PDATASTARTEQU0H PDATALENEQU0H IBPSTACKEQU0; set to 1 if small reentrant is used. IBPSTACKTOPEQU0 xFF 1; default 0FFH1 XBPSTACKEQU0; set to 1 if large reentrant is used. XBPSTACKTOPEQU0 xFFFF 1; default 0FFFFH1 PBPSTACKEQU0; set to 1 if compact reentrant is used. PBPSTACKTOPEQU0 xFF 1; default 0FFH1 PPAGEENABLEEQU0; set to 1 if pdata object are used. PPAGEEQU0 PPAGE_SFRDATA0A0H ; ; Standard SFR Symbols ACCDATA0E0H BDATA0F0H SPDATA81H DPLDATA82H DPHDATA83H NAMEC_STARTUP C_C51STARTUPSEGMENTCODE ;在实际操作中未找到原文中的下在实际操作中未找到原文中的下1行信息行信息 ;PRmainTESTMAINSEGMENTCODE STACKSEGMENTIDATA RSEGSTACK DS1 EXTRN CODE C_START PUBLICC_STARTUP CSEGAT0 C_STARTUPLJMPSTARTUP1 RSEGC_C51STARTUP STARTUP1;该段程序把内存清零该段程序把内存清零 ; 0 MOVR0,IDATALEN - 1 CLRA IDATALOOPMOVR0,A DJNZR0,IDATALOOP ENDIF ; From Disassemble 126 C_STARTUPLJMPSTARTUP1 127 128RSEGC_C51STARTUP 129 130 STARTUP1;该段程序把内存清零该段程序把内存清零 131 132 IF IDATALEN 0 C0 x0000020003LJMPSTARTUP1C0003 133MOVR0,IDATALEN - 1 C0 x0003787FMOVR0,0 x7F 134CLRA C0 x0005E4CLRA 135 IDATALOOPMOVR0,A C0 x0006F6MOVR0,A 136DJNZR0,IDATALOOP C0 x0007D8FDDJNZR0,IDATALOOPC0006 185MOVSP,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 Ba nking Mode 4. 192 EXTRN CODE B_SWITCH0 193CALLB_SWITCH0; init bank mechanism to code bank 0 194 endif 195 ; C0 x0009758107MOVSP0 x81,0 x07 196LJMPC_START C0 x000C02000FLJMPmainC000F 2 ;在实际操作中未找到原文中的下在实际操作中未找到原文中的下3行信息行信息 ;RSEGPRmainTESTMAIN ;main void mainvoid C0 x000F80FESJMPmainC000F;main函数函数 C0 x001100NOP C0 x001200NOP C0 x001300NOP C0 x001400NOP C0 x001500NOP 现在分析上面的汇编程序就会明白 c51程序是如何启动的。 该程序有三个代码段; 第一个代码段C_STARTUP 在0 x0000地址,是 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 SumXYunsigned int X,Y; void mainvoid unsigned int a,b,c; a0 x5500; b0 xaa; while 1 cSumXYa,b; unsigned int SumXYunsigned int X,Y unsigned int Z; ZXY; return Z; 编译后的反汇编代码列表; C0 x0000020027LJMPSTARTUP1C0027 4 void mainvoid 5 unsigned int a,b,c; 6a0 x5500; C0 x0003750855MOV0 x08,0 x55;ram 地址0 x08和0 x09存放变量 a0 x5500。 C0 x0006750900MOV0 x09,0 x00 7b0 xaa; C0 x0009750A00MOV0 x0A,0 x00;ram 地址0 x0A 和0 x0B 存放变量 b0 x00AA。 C0 x000C750BAAMOV0 x0B,0 xAA 8while 1 9 10cSumXYa,b; C0 x000FAD0BMOVR5,0 x0B;寄存器 R4和 R5传递变量 a 的值。 C0 x0011AC0AMOVR4,0 x0A C0 x0013AF09MOVR7,0 x09;寄存器 R6和 R7传递变量 b 的值。 C0 x0015AE08MOVR6,0 x08 C0 x0017120020LCALLSumXYC0020;调用函数 SumXYa,b求 cab C0 x001A8E0CMOV0 x0C,R6;函数 SumXYa,b返回的整型值存在 R6和 R7里, C0 x001C8F0DMOV0 x0D,R7;把返回值存入变量 c,ram 地址0 x0C 和0 x0D 存放变量 c 11 12 13 C0 x001E80EFSJMPC000F 14 unsigned int SumXYunsigned int X,Y 15 unsigned int Z; 16ZXY; C0 x0020EFMOVA,R7;参数变量 X 放在寄存器 R6和 R7里 C0 x00212DADDA,R5;参数变量 Y 放在寄存器 R4和 R5里 C0 x0022FFMOVR7,A C0 x0023EEMOVA,R6 C0 x00243CADDCA,R4;计算 ZXY; C0 x0025FEMOVR6,A;局部变量 Z 也放在寄存器 R6和 R7里 17return Z由寄存器 R6和 R7里返回函数的值 C0 x002622RET 151MOVSP,STACK-1 152 ; This code is required if you use L51_BANK.A51 with Banking Mode 4 153 ; EXTRN CODE B_SWITCH0 154 ;CALLB_SWITCH0; init bank mechanism to code bank 0 C0 x002775810DMOVSP0 x81,0 x0D 155LJMPC_START C0 x002A020003LJMPmainC0003 /*/ 内容三内容三函数的入口地址,如何调用汇编函数,函数的入口地址,如何调用汇编函数,c 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 SumXYunsigned int X,Y; extern void Delayunsigned char T; void mainvoid unsigned int a,b,c; a0 x5500; b0 x00aa; while 1 Delay100; cSumXYa,b; 混合编程文件 funcasm.c c 和汇编的混合编程演示. 注意要把汇编语言函数放在文件前面。 求 ZXY 的汇编语言函数,Z,X,Y 是整型数。 pragma ASM PUBLIC_SumXY PR_SumXYFUNCASMSEGMENT CODE RSEGPR_SumXYFUNCASM _SumXY;求 ZXY MOVA,R7;参数 X 放在寄存器 R6和 R7里 ADDA,R5;参数 Y 放在寄存器 R4和 R5里 MOVR7,A MOVA,R6 ADDCA,R4;扑鉠XY; MOVR6,A;局部变量 Z 也放在寄存器 R6和 R7里 RET pragma ENDASM c 语言函数,延时函数。 void Delayunsigned char T unsigned char i; for i0;iT;i for i0;iT;i 上面程序编译后的反汇编代码列表; C_STARTUP C0 x0000020041LJMPSTARTUP1C0041 main 给变量 a 和 b 赋值 a0 x5500;b0 x00aa; C0 x0003750855MOV0 x08,0 x55 C0 x0006750900MOV0 x09,0 x00 C0 x0009750A00MOV0 x0A,0 x00 C0 x000C750BAAMOV0 x0B,0 xAA 调用延时函数 Delay100; C0 x000F7F64MOVR7,0 x64 C0 x0011120025LCALLDELAYC0025 求 cSumXYa,b; C0 x0014AD0BMOVR5,0 x0B C0 x0016AC0AMOVR4,0 x0A C0 x0018AF09MOVR7,0 x09 C0 x001AAE08MOVR6,0 x08 C0 x001C12003ALCALLSUMXYC003A;调用汇编语言函数 SumXYunsigned int X,Y C0 x001F8E0CMOV0 x0C,R6 C0 x00218F0DMOV0 x0D,R7 C0 x002380EASJMPC000F c 语言延时函数的反汇编代码 void Delayunsigned char T DELAY C0 x0025E4CLRA C0 x0026FEMOVR6,A C0001 C0 x0027EEMOVA,R6 C0 x0028C3CLRC C0 x00299FSUBBA,R7 C0 x002A500DJNCC0007C0039 C0 x002CE4CLRA C0 x002DFEMOVR6,A C0004 C0 x002EEEMOVA,R6 C0 x002FC3CLRC C0 x00309FSUBBA,R7 C0 x00315003JNCC0003C0036 C0 x00330EINCR6 C0 x003480F8SJMPC0004C002E C0003 C0 x00360EINCR6 C0 x003780EESJMPC0001C0027 C0007 C0 x003922RET 汇编语言函数 SumXYunsigned int X,Y的反汇编代码,求 ZXY SUMXY C0 x003AEFMOVA,R7 C0 x003B2DADDA,R5 C0 x003CFFMOVR7,A C0 x003DEEMOVA,R6 C0 x003E3CADDCA,R4 C0 x003FFEMOVR6,A C0 x004022RET 程序启动代码; STARTUP1 C0 x004175810DMOVSP0 x81,0 x0D C0 x0044020003LJMPmainC0003 参数传递规则例子 fun1int aa 是第一个参数,在 R6,R7中传递. fun2int b ,int c , int *db 在 R6,R7中传递;C 在 R4,R5中传递;d 在 R1,R2,R3中传递. fun3long e ,long fe 在 R4-R7中传递;f 不能在寄存器中传递,只能在参数传递段中传递. fun3float g ,char hg 在 R4-R7中传递;h 不能在寄存器中传递,只能在参数传递段中传递 C 语言和汇编语言的变量以及函数的接口问题语言和汇编语言的变量以及函数的接口问题 在 C 和汇编混合编程的时候,存在 C 语言和汇编语言的变量以及函数的接口问题。 在 C 程序中定义的变量,编译为.asm 文件后,都被放进了.bss 区,而且变量名的前面都带了一个下划线。在 C 程 序中定义的函数,编译后在函数名前也带了一个下划线。例如 extern int num就会变成.bss _num, 1 extern float nums5 就会变成.bss _nums, 5 extern void func 就会变成_func, 一一汇编和汇编和 C C 的相互调用可以分以下几种情况的相互调用可以分以下几种情况 (1)汇编程序中访问 c 程序中的变量和函数。 在汇编程序中,用_XX 就可以访问 C 中的变量 XX 了。访问数组时,可以用_XX偏移量来访问,如_XX3 访问了数组 中的 XX3。 在汇编程序调用 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 接口中寻址方式的改变接口中寻址方式的改变 需要注意的是,在 C 语言中,对于局部变量的建立和访问,是通过堆栈实现的,它的寻址是通过堆栈寄存器 SP 实 现的。而在汇编语言中,为了使程序代码变得更为精简,TI 在直接寻址方式中,地址的低 7 位直接包含在指令中, 这低7位所能寻址的具体位置可由DP寄存器或SP寄存器决定。 具体实现可通过设置ST1寄存器的CPL位实现, CPL0, DP 寻址,CPL1,SP 寻址。在 DP 寻址的时候,由 DP 提供高 9 位地址,与低 7 位组成 16 位地址;在 SP 寻址的时候, 16 位地址是由 SP16 位与低 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_buffBUFF_SIZE;DSP 发向 PC 机的数据 int to_dte_num;缓冲区中存放的有效字节数 int to_dte_store;