240 likes | 406 Views
第 4 章 程序设计. §4 .1 汇编语言程序设计. [ 例 4.1] 程序每隔一段时间从 A 口输出数据,驱动 LED 亮。. .INCLUDE handware.inc .RAM .VAR R_LedCounter; .VAR R_DelayCounter; .CODE .PUBLIC _main; _main: R1=0xFFFF; [P_IOA_Dir]=R1; [P_IOA_Attrib]=R1; R1=0x0000; [P_IOA_Data]=R1; [R_LedCounter]=R1;. L_MainLoop:
E N D
[例4.1] 程序每隔一段时间从A口输出数据,驱动LED亮。 .INCLUDE handware.inc .RAM .VAR R_LedCounter; .VAR R_DelayCounter; .CODE .PUBLIC _main; _main: R1=0xFFFF; [P_IOA_Dir]=R1; [P_IOA_Attrib]=R1; R1=0x0000; [P_IOA_Data]=R1; [R_LedCounter]=R1; L_MainLoop: R1= [R_LedCounter]; [P_IOA_Data]=R1; R1= [R_LedCounter]; R1+=1; [R_LedCounter]=R1; R1=0; [R_DelayCounter]=R1; CALL F_Delay; JMP L_MainLoop;
全局标号与局部标号 • 标号 是一种符号地址。是指令或数据的起始地址。 • 全局标号 各程序段都能调用。以字母或“_”开头,以“:”结束。 • 局部标号 只在该程序段调用。以“?”为前缀或后缀。
[例4.3] 程序每隔一段时间从A口输出数据,驱动LED亮。 LED显示什么? .INCLUDE handware.inc .RAM .VAR R_LedCounter; .VAR R_DelayCounter; .CODE .PUBLIC _main; _main: R1=0xFFFF; //PIOA口为同相低电平输出 [P_IOA_Dir]=R1; [P_IOA_Attrib]=R1; R1=0x0000; [P_IOA_Data]=R1; [R_LedCounter]=R1; //清变量R_LedCounter L_MainLoop: R1= [R_LedCounter]; //从变量R_LedCounter处获取 [P_IOA_Data]=R1; //显示值,显示。 R1= [R_LedCounter]; //修改变量R_LedCounter, R1+=1; [R_LedCounter]=R1; //使变量R_LedCounter加1。 R1=0; [R_DelayCounter]=R1; //清变量R_DelayCounter CALL F_Delay; JMP L_MainLoop; //*********延时子程序。 ***********************// F_Delay: L_DelayLoop: R1=0x0001; //4 [P_Watchdog_Clear]=R1; //7 R1=[R_DelayCounter]; //7,变量R_DelayCounter+1 R1+=1; //3 [R_DelayCounter]=R1; //7 JNZ L_DelayLoop; //5 RETF; 什么寻址方式?
[例4.4]冒泡排序 .IRAM Array: .DW 40, 6, 32, 12, 9, 24, 28; .VAR C_Flag; .CODE .PUBLIC _main; _main: L_Sort: BP=Array; R1=0x0006; R4=0; //进行下轮排序之前,清标志单元。 [C_ Flag]=R4; L_Loop: R3=[BP]; //相邻单元比较:取第一个数, CMP R3, [BP+1]; //与第二个数比较。 什么寻址方式? 程序要进行多少次L_Sort循环? JB L_Next; //小于转移,顺序不变。 R2=[BP+1]; //否则,交换顺序: [BP]=R2; //较小值存于第一单元 [BP+1]=R3; //较大值存于第二单元。 R3=0x0001; //置位标志单元。 [C_Flag]=R3; L_Next: BP=BP+1; //BP指向下第二个单元。 R1-=1; //计数器减1。判断是否到最后一个单元? JNZ L_Loop; //否,继续比较余下的数。 R4=[C_Flag]; //上述排序结束。标志单元=0? JNZ L_Sort; //0,可能排序未结束。再进行一次。 L_MainLoop: JMP L_MainLoop; 原顺序:40,6,32,12,9,24,28 排序 1 :6,32,12,9,24,28,40 排序 2 :6,12,9,24,28,32,40 排序 3 :6,9,12,24,28,32,40
关于子程序 • 子程序的结构 子程序名 .PROC 程序体 RETF .ENDP • 现场保护-返回地址、寄存器。 • 参数传递 • 通过寄存器传递—R1~R4 • 通过变量传递—不同文件使用变量时,PUBLIC声明全局变量,使用此变量的其它文件,应该用EXTERNAL声明为外部变量。 • 通过堆栈传递
//函数:F_Abs_32() //语法: void F_Abs_32( int A , int B ) //描述: 求32位符号数的绝对值 //参数: R4R3=符号数 //返回: R2R1=绝对值 //===================================== .CODE .PUBLIC F_Abs_32 F_Abs_32: R1=R3; R2=R4; JMI ?neg;//为负转移 RETF; ?neg: R1^=0xFFFF;//取反 R2^=0xFFFF; R1+=1; //加1 R2+=0,Carry; RETF; [例4.5] 求32位符号数的绝对值。
数据类型 数据长度(位数) 值域 Int 16 -32768~+32767 long int 32 -2147483648~+2147483647 unsigned int 16 0~65535 Unsigned long 32 0~4294967295 float 32 以IEEE 格式表示的32位浮点数 double 64 以IEEE 格式表示的64位浮点数 §4.2C语言程序设计 GNC CC是一个功能非常强大的跨平台 C 编译器,它对 C 语言提供了很多扩展,这些扩展对优化、目标代码布局、更安全的检查等方面提供了很强的支持。 • 关于GCC • GCC对ANSI-C基本数据类型的支持
[例4.6]用C语言编写用IO控制闪灯程序 #include “hardware.h” void Delay() { unsigned int I ; for (I=0; I<32768; I++){} } int main() { int LedCounter=0; int temp; SP_Init_IOA(0xffff, 0xffff, 0xffff) while(1) { SP_Export(Port_ioa_Data, LedCounter); LedCounter++; Delay(); } } //////////////////////////////////////////////////////////////// // Function: I/O Port A configuration // void SP_Inti_IOA(int Dir, int Data, int Attrib) //////////////////////////////////////////////////////////////// _SP_Init_IOA: .PROC PUSH BP TO [SP] BP = SP + 1 PUSH R1 TO [SP] R1 = [BP+3] // Port direction [P_IOA_Dir] = R1 R1 = [BP+4] [P_IOA_Data] = R1 R1 = [BP+5] [P_IOA_Attrib] = R1 POP R1 FROM [SP] POP BP FROM [SP] RETF .ENDP 与[例4.3]程序功能比较,有何异同? _SP_Export: .PROC PUSH BP,BP TO [SP] BP = SP + 1 PUSH R1,R2 TO [SP] R1 = [BP+3] // Port Number R2 = [BP+4] // Value [R1] = R2 //输出 POP R1,R2 FROM [SP] POP BP,BP FROM [SP] RETF .ENDP
§4.3.2 C与汇编语言的交叉调用 参数 • 1、程序调用协议: • 调用子程序间的参数传递; • 调用子程序或函数的参数传递(堆栈); • 子程序或函数的返回值 (R2R1); • 堆栈结构与维护。 • 由于C编译器产生的所有标号都以下划线“_”为前缀,而C程序在调用汇编程序时要求汇编程序名也以下划线“_”为前缀。 返回地址 子程序或 函数空间
C语言调用汇编程序 main( ) { int ret=0 ret=init_IOA(0x0000, 0x00FF, 0xFFFF); if (ret==0x01); } .public _init_IOA _init_IOA: push bp to [sp] bp=sp+1 r1=[bp+3] r2=[bp+4] r3=[bp+5] …… r1=0x01 //返回值。 pop bp from [sp] retf //取第1个参数 //取第2个参数 //取第3个参数 调用后SP //处理程序 bP 函数调用后的返回值,16位放在R1中,32位放在R2、R1中。 第1个参数 第2个参数 第3个参数 调用前SP
.code//===================================//函数: F_Sub_Asm1()//语法:void F_Sub_Asm1(void)//描述:整形返回值测试//参数:无//返回:整形值。//====================================.PUBLIC _F_Sub_Asm1_F_Sub_Asm1:R1 = 0xaabb;R2 = 0x5555;RETF;//====================================//函数: F_Sub_Asm2()//语法:void F_Sub_Asm2(void)//描述:长整型值返回值测试//参数:无//返回:一个长整型值//======================================.PUBLIC _F_Sub_Asm2_F_Sub_Asm2:R1 = 0xaabb;R2 = 0xffcc;RETF; [例4.11] //***************************************************************/// 描述: 测试函数的返回值// 日期: 2002/12/11//***************************************************************/int F_Sub_Asm1(void); //声明要调用的函数的函数原型long int F_Sub_Asm2(void); //声明要调用的函数的函数原型//===============================================================// 函数: main()// 描述:主函数//===============================================================int main(){int i;long int j;while(1){i = F_Sub_Asm1();j = F_Sub_Asm2();}return 0;}
//中断程序ISR.ASM.PUBLIC _IRQ5.INCLUDE hardware.inc.EXTERNAL _TimeCount; //计时.TEXT//========================//函数: IRQ5()//语法:void IRQ5(void)//描述:IRQ5中断服务程序//参数:无//返回:无//======================== [例4.12]在IOA口上以2S的速率闪烁 //============================//函数: F_InitIOA()//语法:void F_InitIOA(void)//描述:IO口初始化//参数:无//返回:无//============================.PUBLIC _F_InitIOA; //初始化IOA口_F_InitIOA: .PROCPUSH BP TO [SP];BP=SP+1;R1=[BP+3];[P_IOA_Dir]=R1;R1=[BP+4];[P_IOA_Attrib]=R1;R1=[BP+5];[P_IOA_Data]=R1;POP BP FROM [SP];RETF;.ENDP; ? //************************************/// 描述: C语言与汇编混合编程举例//************************************/unsigned int TimeCount = 0;int main(){TimeCount = 0; F_InitIOA(0xFFFF,0xFFFF,0x0000); //初始化IOA口带数据缓冲低电平输出SystemInit(); //系统初始化while(1) { if(TimeCount<=4) LightOff(); //IOA口LED熄灭else if(TimeCount<=7) LightOn(); //IOA口LED亮elseTimeCount=0; }} //System.asm 汇编程序.INCLUDE hardware.inc.CODE//============================//函数: SystemInit()//语法:void SystemInit(void)//描述:系统初始化//参数:无//返回:无//===========================.PUBLIC _SystemInit; //系统初始化_SystemInit: .PROCR1=0x0004 //开2Hz中断[P_INT_Ctrl]=R1IRQ ONRETF;.ENDP; _IRQ5:PUSH R1,R5 TO [SP]R1 = 0x0008;TEST R1,[P_INT_Ctrl];JNZ L_IRQ5_4Hz;L_IRQ5_2Hz: //2Hz中断R1=0x0004[P_INT_Clear] = R1; //清中断R1=[_TimeCount] //计数器+1R1+=1[_TimeCount]=R1POP R1,R5 FROM [SP];RETI;L_IRQ5_4Hz: //4Hz中断[P_INT_Clear] = R1;POP R1,R5 FROM [SP];RETI; //=============================//函数: LightOff()//语法:void LightOff(void)//描述:熄灭led//参数:无//返回:无//=============================.PUBLIC _LightOff; //IOA口LED熄灭_LightOff: .PROCR1= 0x0000;[P_IOA_Data] = R1;RETF;.ENDP //==========================//函数: LightOn()//语法:void LightOn(void)//描述:点亮led//参数:无//返回:无//==========================.PUBLIC _LightOn; //IOA口LED点亮_LightOn: .PROCR1= 0xFFFF;[P_IOA_Data] = R1;RETF;.ENDP
//====================================== //函数: F_Sub_C()//语法:void F_Sub_C(int i,int j,int k)//描述:延时程序//参数:i,j,k//返回:i//======================================int F_Sub_C(int i,int j,int k)//i=3,j=2,k=1。 { i++; j++; k++; return i;} //**********************************/// 描述: 汇编调用C的函数// 日期: 2002/12/10//**********************************/.EXTERNAL _F_Sub_C.CODE.PUBLIC _main;//==================================// 函数: main()// 描述:主函数//==================================_main:R1 = 1;PUSH R1 TO [SP]; //第3个参数入栈R1 = 2;PUSH R1 TO [SP]; //第2个参数入栈R1 = 3;PUSH R1 TO [SP]; //第1个参数入栈CALL _F_Sub_C;POP R1,R3 FROM [SP]; //弹出参数回复SP指针GOTO _main;RETF; 汇编语言程序调用C函数 返回值还在吗? 修改指令: POP R2,R4 FROM [SP] 只有一个空闲寄存器,比如R4: POP R4 FROM [SP] POP R4 FROM [SP] POP R4 FROM [SP] 例【 4.13】 增加指令:R4=R1
作业 P129 3、4、8
本章小节 • 程序设计-I/O端口初始化,数字计算与处理。 • 汇编语言程序设计 • C与汇编交叉调用 • 阅读程序
§4.3.1C语言的在线汇编 • 指令格式 asm (“汇编指令模板”:输出参数:输入参数:clobbers参数); 例、 asm (“%0+=%1”:“+r”(foo):“r”(bar)); • 汇编指令摸板 说明当前汇编指令。如, “%0+=%1”: 其中%0、 %1为形参,数字为冒号后操作数序号。将分别被输入输出参数替代。 • 操作数—上例中, (foo)、 (bar) • 约束符—上例中, “+r”、“r”
在线汇编举例 [例4.7]、 asm(”%0=%1+%2”:”=r”(foo):”r”(bar),”i”(10)); 汇编代码:R1=R4+10 [例4.8]、 asm(“.include hareware.inc”); //要使用端口名时,打开此文件。 asm(“[P_IOB_Attrib]=%0\n\t” “[P_IOB_Data]=%1\n\t” “[P_IOB_Dir]=%0\n\t” : : “r”(0), “r”(0xffff) );//初始化B口为带上拉电阻的输入。 [例4.9]、…… int temp; asm (“.include hareware.inc”); asm(“%0= [P_IOB_Data]”: “=r”(temp)); asm( “[P_IOA_Buffer]=%0”:: “r” (temp)); 将B口的数据 temp; temp A口输出。 没有输出参数。 一个指令模板挂接多条汇编指令时,用\N结束每条指令。用\t来对齐模板中的各指令。
汇编程序调用C语言 ? 弹出已不用参数,清理堆栈垃圾,恢复指针。 int addnum(int m,int n) //第1个参数送m,第2个参数送n。 { int j=0; int i=0; for (i=1; i<=m; i++) j=j+n; return(j) } EXTERNAL _addsum .CODE .public _main _main: r1=0x55 //第1个参数 r2=0x20 //第2个参数 push r1,r2 to [sp] call _addsum r3=r1 //返回值 pop r1,r2 from [sp] loop: r1=0 jmp loop • 在汇编函数中要调用C语言的子函数,应该根据C的函数原型所要求的参数类型,分别把参数压入堆栈后,再调用C函数。调用结束返回后还需再进行出栈操作,以恢复调用C函数前的堆栈指针。 调用后SP 调用前SP 第1个参数 第2个参数