1、C和ASM的混合编程,jianghq 2003-5 Tsinghua DEE,单片机与嵌入式系统,Content,程序的结构 汇编程序 C程序 project框架 如何改写汇编程序? 在C程序中如何做一些底层操作?,存储空间结构,程序的结构,汇编程序结构,C程序结构,M30624FGFP Memory Map,几个主要的参数,RAM区ramTOPramENDROM区progTOPVvector,汇编程序结构段类型,RAM DATAROM CODEROMDATA,汇编程序结构 段定义,.SECTION memory,DATA ; 数据段.ORG VramTOP.SECTION prog,CODE
2、 ; 代码段 .ORG VprogTOP.SECTION val_vect,ROMDATA ;用户自定义中断向量段.ORG Vintbase.SECTION inter,ROMDATA ;固定中断向量段.ORG Vvector+(8*4),汇编程序中几个主要的参数,VramTOP .EQU 000400H VramEND .EQU 002BFFH VIstack .EQU 002C00H ISP VprogTOP .EQU 0F0000H Vintbase .EQU 0FA000H Vvector .EQU 0FFFDCH SB_base .EQU 000380H SB,关于SB和FB,.SB
3、 SB_base.SBSYM recv_nums 告诉编译器使用该变量的指令寻址方式采用相对基址寻址方式,可以提高生成代码的效率。,关于堆栈,两种堆栈:堆栈指针ISP和USP 使用何种堆栈,由堆栈指针选择标志位(U标志)选取。U = 0 选取中断堆栈指针ISPU = 1 选取用户堆栈指针USP 响应中断后,该标志清0,执行中断子程序SP指针位置如何设定: 栈顶指针Push : SP = SP 1Pop : SP = SP + 1,C程序结构,RAM DATA databssstackheapROM CODE program ROMDATA romvectorfvector,NC编译器对Sect
4、ion的处理,Section的属性,属性:I(是否初值), N/F(扩展区) S(SB寻址区), E/O(偶地址对齐) 举例:bss_SE, bss_NE, data_FE, data_SEI,data_SEbss_SEdata_SObss_SOdata_NEbss_NEdata_NObss_NOstackheapdata_FEbss_FE data_FOdata_FE,C程序中所有的section列表,DATA,rom_NErom_NOrom_FErom_FOdata_SEIdata_SOIdata_NEI data_NOI data_FEI data_FOIvectorfvector,RO
5、MDATA,CODE,programinterruptprogram_S,根据芯片修改sect30.inc,注释掉不存在的段最重要的三个段的位置设定.section data_SE,DATA.org 400H.section rom_FE,ROMDATA.org 0C0000H.section vector,ROMDATA.org VECTOR_ADR VECTOR_ADR .equ 0FA000H 其他的段就交给编译器来处理,例子:一个Section分配的结果,C程序结构 project,可能包括多个asm文件, C文件如何安排project的结构?,程序的入口,首先要清楚整个程序的执行过程
6、从哪里开始执行?,Startup文件,sect30.inc文件,; fixed vector section.org 0fffdch NMI:.lword dummy_int RESET:.lword start.end,Startup文件,ncrt0.a30文件,.glb start.section interrupt start: ;- ; after reset,this program will start ;-ldc #istack_top, isp ;set istack pointer,ncrt0.a30设定单片机工作模式 RAM 清0 变量初值设定 堆初始化 调用全局Init子
7、程序 调用全局Main子程序 exit循环,启动过程,主C程序Void Init() Void Main() ,主C文件: init() int1(); int2();int3();main() sub1();sub4(); ,Project层次框架,Startup文件: ncrt0.a30,系统提供的库程序: #include “stdlib.h”,ASM子程序:init1(); sub1(),C子程序1:init2(); sub2(); sub3(),C子程序2:init3(); sub4();,ASM子程序:init4(); sub5(),一个project的层次图,程序结构小结,明白各
8、个Section的含义根据芯片参数和自己的程序需要来修改startup程序用层次化来保持整个组织结构的清晰,如何改写汇编程序?,目标: 将他人提供的汇编程序转为可供自己在C程序中调用的子函数。例子:红外接收的子程序,Step1,大致看懂原程序 完成了一个什么样的功能。 主要的几个变量是什么含义。,Step2,分析自己的要求 如果将该汇编程序独立出来作为一个子函数; 那么其功能是什么? 输入参数:有哪些 返回值: 有哪些我希望实现的功能 等待接收一个字节,如果收到立刻返回该字节,没收到且时间超过300ms,那么也返回,同时指示状态为未接收。char recv_status = IR_RECV(c
9、har userID, char* pByte); 指示接收状态 接收用户码 接收字节地址,Step3,对原来的程序进行裁减 删去原有的各个段定义 重新定义各个Section: bss_SE, rom_FE, program 定义和声明全局子函数 _IRR_INIT _IR_RECV I_timera0 涉及C和汇编子程序之间的参数传递(详见下文) 删除不必要的变量和程序 如果使用中断,则将中断向量表设置挪至sect30.inc 最后以.end结束文件,C和汇编子程序之间的参数传递,参数规则(从C到汇编) 参数规则(从汇编返回到C) 函数名定义规则,参数规则(从C到汇编),采用#pragma
10、PARAMETER,参数规则(从汇编到C),函数名定义规则,最后结果,C程序部分: extern char IR_RECV(char,char*); #pragma PARAMETER IR_RECV(R0L,A0)汇编语言部分:.glb _IR_RECV ; 全局调用声明 _IR_RECV:mov.b R0L,N_CSTM ; 参数传递:用户码mov.w A0, SAVE_ADDR ; 参数传递:接收结果地址 mov.b #1, R0L ; 返回1,如果收到了数据,改写汇编程序小结,看懂源程序在明白自己所需要的功能后,再进行修改 有的时候还是需要改多一些地方。该例子存放到ftp:/166.1
11、11.172.7,在C程序中如何做一些底层操作,特殊寄存器的操作BIT操作,特殊寄存器的操作,方法1: 使用#pragma ADDRESS#pragma ADDRESS TA0 0386H #pragma ADDRESS TABSR 0380H 建议将所有SFR地址声明放在一个文件里 #include “mcu16c62.h”定义类型(根据其所占的内存大小) char TA0MR,TA0IC,TABSR; unsigned int TA0;,特殊寄存器的操作,方法2:嵌入汇编语句asm()尤其是一些系统寄存器R0, FLG 方法3:定义成汇编子函数另写一个as30文件,将相关的操作封装成函数,
12、供C程序调用 方法4:直接定义指针,并对该指针赋初值,BIT操作,使用位域,BIT操作,使用宏 #define SET_BIT(n,byteA) byteA |= (0x01n); /* Set Bit =1 */ #define CLR_BIT(n,byteA) byteA /* Set Bit =0 */ #define TST_BIT(n,byteA) (byteA&(0x01n)=0 /* Bit = 0 ? */,程序编完了,程序编完了,还要考虑哪些问题?A. 是否用了动态分配内存的函数 - HEAPSIZEmalloc, allot,.如果用了,则Heapsize够不够。没用,。注释掉,节约资源。 B. 堆栈的大小够否? - STACKSIZE, ISTACKSIZE可直接用Stack Calulate (TM工具栏上)查看这两个参数的设定都在ncrt0.a30中,