1 / 24

第 4 章 程序设计

第 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:

ellery
Download Presentation

第 4 章 程序设计

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 第4章 程序设计

  2. §4.1 汇编语言程序设计

  3. [例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. 数制及数据类型

  5. µ’nSPTM算符及优先次序

  6. 全局标号与局部标号 • 标号 是一种符号地址。是指令或数据的起始地址。 • 全局标号 各程序段都能调用。以字母或“_”开头,以“:”结束。 • 局部标号 只在该程序段调用。以“?”为前缀或后缀。

  7. 汇编语言程序举例

  8. [例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; 什么寻址方式?

  9. [例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

  10. 关于子程序 • 子程序的结构 子程序名 .PROC 程序体 RETF .ENDP • 现场保护-返回地址、寄存器。 • 参数传递 • 通过寄存器传递—R1~R4 • 通过变量传递—不同文件使用变量时,PUBLIC声明全局变量,使用此变量的其它文件,应该用EXTERNAL声明为外部变量。 • 通过堆栈传递

  11. //函数: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位符号数的绝对值。

  12. 数据类型 数据长度(位数) 值域 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基本数据类型的支持

  13. [例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

  14. §4.3 C和汇编混合编程

  15. §4.3.2 C与汇编语言的交叉调用 参数 • 1、程序调用协议: • 调用子程序间的参数传递; • 调用子程序或函数的参数传递(堆栈); • 子程序或函数的返回值 (R2R1); • 堆栈结构与维护。 • 由于C编译器产生的所有标号都以下划线“_”为前缀,而C程序在调用汇编程序时要求汇编程序名也以下划线“_”为前缀。 返回地址 子程序或 函数空间

  16. 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

  17. .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;}

  18. //中断程序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

  19. //====================================== //函数: 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

  20. 作业 P129 3、4、8

  21. 本章小节 • 程序设计-I/O端口初始化,数字计算与处理。 • 汇编语言程序设计 • C与汇编交叉调用 • 阅读程序

  22. §4.3.1C语言的在线汇编 • 指令格式 asm (“汇编指令模板”:输出参数:输入参数:clobbers参数); 例、 asm (“%0+=%1”:“+r”(foo):“r”(bar)); • 汇编指令摸板 说明当前汇编指令。如, “%0+=%1”: 其中%0、 %1为形参,数字为冒号后操作数序号。将分别被输入输出参数替代。 • 操作数—上例中, (foo)、 (bar) • 约束符—上例中, “+r”、“r”

  23. 在线汇编举例 [例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来对齐模板中的各指令。

  24. 汇编程序调用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个参数

More Related