400 likes | 662 Views
第 4 章 汇编语言程序设计. 4.1 伪指令 4.1.1 为什么要使用伪指令 4.1.2 MCS - 51 单片机的伪指令 4.2 汇编语言程序设计 4.2.1 顺序结构 4.2.2 分支程序 4.2.3 循环结构 4.2.4 子程序设计和子程序调用. 4.1.1 为什么要使用伪指令. 前一章详细介绍了 MCS - 51 单片机的 111 条汇编语句,但是编程时仅利用这些汇编语句还不够,我们还会有这样的需求: ◆ 指示程序开始和结束 ◆ 分配存储空间 ◆ 定义变量和常量 ◆ 定义数组
E N D
第4章 汇编语言程序设计 4.1 伪指令 4.1.1 为什么要使用伪指令 4.1.2 MCS-51单片机的伪指令 4.2 汇编语言程序设计 4.2.1 顺序结构 4.2.2 分支程序 4.2.3 循环结构 4.2.4 子程序设计和子程序调用
4.1.1 为什么要使用伪指令 前一章详细介绍了MCS-51单片机的111条汇编语句,但是编程时仅利用这些汇编语句还不够,我们还会有这样的需求: ◆ 指示程序开始和结束 ◆ 分配存储空间 ◆ 定义变量和常量 ◆ 定义数组 汇编程序如何了解我们的安排,按我们的要求来做? 答案:依靠伪指令。
4.1.2 MCS-51单片机的伪指令 伪指令不是真正的指令,无对应的机器码,在汇编时不产生可供CPU直接执行的机器码(即目标程序),只是用来对汇编过程进行某种控制。 MCS-51单片机常用伪指令有下面几种:
1 ORG(汇编起始命令) origin 格式: [标号:] ORG 16位地址或标号 例: ORG 0000H 常用于汇编语言源程序或数据块开头。 功能:将该伪指令后的程序机器码按照ORG伪指令提供的16位地址或标号存入相应的程序存储器中。即规定目标程序在程序存储器中存放的起始地址。 8086中还有 段定义伪指令SEGMENT 过程定义语句PROC 模块定义伪指令NAME 【注】在用户程序中,ORG伪指令仅需要用于设置主程序入口地址、复位和中断服务入口地址,其他子程序不必使用ORG伪指令。
ORG 0050H CWORD: MOV DPTR,#7000H MOVX A,@DPTR MOV B,A SWAP A ANL A,#0FH INC DPTR MOVX @DPTR,A MOV A,B ANL A,#0FH INC DPTR MOVX @DPTR,A CWORD1:SJMP CWORD1 END
2 END(汇编结束命令) 格式: [标号:] END 功能:通知汇编程序结束汇编。在END之后所有的汇编指令均不予以汇编成机器码。 【注】END伪指令必须放在所有用户程序的后面,不是放在主程序的后面,而且只能有一个。
3 EQU(赋值命令) equal 8086中还有 “=”伪指令 格式: 字符名称 EQU 数据或汇编符号 功能:将数据或汇编符号赋值给所定义的字符名,字符名称和数据或汇编符号完全等价。字符名称可以作为数据(常量)、地址(变量),可以是8位的,也可以是16位的。 【注】EQU定义的字符名称必须先定义后使用。
例: A10 EQU 30H ………. Main: ………… MOV A, #A10 MOV A, A10 ; 即MOV A, #30H ; 即MOV A, 30H A10表示片内30H单元地址
4 DATA(数据地址赋值命令) 格式为: 字符名称 DATA 表达式 功能:与EQU类似,使字符名称和表达式完全等价。这两条伪指令的主要区别在于: EQU定义的字符名称必须先定义后使用, DATA定义的字符名可以先使用后定义。
5 DB(定义字节命令) 8086中为 【变量名】 DB 表达式 格式为: [标号:] DB项或项表 项或项表可以是一个8位二进制数据、用逗号分开的一串8位二进制数据、括在单引号中的ASCII字符串。 功能:用于定义8位数据或字符的存放地址。将DB伪指令后的8位项或项表数据依次存放到以左边标号为起始地址的连续存储单元中,(若没有标号,则从该DB伪指令的汇编地址开始)。类似于VB中数组的定义。常用于定义数据表格,程序中使用查表方法将数据取出。
… 0A3H TABLE=1000H 1001H 26H 1002H 03H 1003H 41H 1004H 42H 1005H 43H … … 例:设 TABLE为1000H MAIN: MOV A,#2H ;待转换的数送A MOV DPTR,# TABLE ;表首地址送DPTR MOVC A,@A+DPTR ;查表(A)=((A)+(DPTR))=(1002H) MOV R3,A ;查表结果送R3 … TABLE:DB 0A3H ;项或项表为一个8位二进制数据 DB 26H, 03H ;项或项表为用逗号分开的 一串8位二进制数据 DB ‘ABC’ ;项或项表为括在单引号中的ASCII字符串, A,B,C的ASCII码分别为41H,42H,43H 【注】1、当一串8位二进制数据超过一行后,下一行必须以DB开始 而不可: DB 0A3H,01H,02H,03H,04H,05H 26H,03H 2、对ASCII字符串可以采用‘ABC’或‘A’, ‘B’ ,‘C’ 8086中为 MOV AL,2H MOV BX,OFFSET TABLE XLAT 03H
6 DW(定义字命令) 8086中为 【符号名】 DW 表达式 格式为: [标号:] DW 16位数据项或项表 功能:用于定义16位数据的存放地址。把DW后的16位数据项或项表依次存入以左边标号为起始地址的连续存储单元中,(若没有标号,则从该DW伪指令的汇编地址开始)。常用于定义一个地址表。 16位数据项或项表采用高8位在前,低8位后在后原则存放。
… 12H TABLE=1000H 1001H 34H 1002H 00H 1003H 23H 1004H 00H 1005H 10H … … 例:TABLE:DW 1234H,23H,10H 假设经汇编后,TABLE对应的程序存储器地址单元为1000H,那么从1000H单元开始依次存放如下: (1000H)= (1001H)= (1002H)= (1003H)= (1004H)= (1005H)= 采用DW伪指令,两个逗号之间的数据表示16位二进制数,即23H是表示0023H,所以(1002H)=00H,而不是(1002H)=23H 【注】与DB伪指令一样,当16位数据项或项表超过一行后,下一行同样以DW开始。 12H 34H 00H 23H 00H 10H
… TABLE=1000H 1001H 1002H 1003H 1004H 30H 1005H 8AH … … 7 DS 定义存储空间命令 格式:[标号:] DS 表达式 功能:用于定义从标号开始预留一定数量的内存单元,以备程序执行过程中使用。预留单元的数量由表达式决定。 例:TABLE: DS 04H DB 30H,8AH 8086中用 TABLE DB ?,?,?,? 来预留4个单元存储空间
8 BIT ( 位地址符号命令) 格式: 字符名 BIT 位地址 功能:把BIT后的位地址赋给字符名,使字符名和位地址等价。 例如: LED1 BIT P1.0 FLAG BIT 02H 汇编后,P1口第0位的位地址90H就等价于LED1,而位寻址空间位地址02H等价于FLAG。 【注】与EQU指令功能类似,两种区别在于 EQU伪指令是对字节常量或变量的定义, BIT伪指令是对位变量的定义。
4.2 汇编语言程序设计 P65 4.2.1 顺序结构 4.2.2 分支程序 4.2.3 循环结构 4.2.4 子程序设计和子程序调用
一、汇编语言程序设计基本步骤 分析问题-> 绘制流程图->编写程序->检查和调试 二、汇编语言程序的基本结构 程序的基本结构有4种:顺序、分支、循环和子程序
… 7000H 7001H 7002H … 实验二:拆字实验 要求:把7000H单元内的内容拆开,高4位送7001H,低4位送7002H。用于将数据送显示缓冲区显示。 分析思路设(7000H )=34H 3 4 H 0011 0100 B 34H 03H 0011 0100 04H 3 H 4 H
设(7000H )=34H,怎么获得03H和04H? 4 3 H 3 4 H 4 3 0 H 0 H SWAP 4 H 3 0 0 1 1 0 1 0 0B 0 1 0 0 0 0 1 1B ANL ∧ ∧ 0 0 0 0 1 1 1 1B 0 0 0 0 1 1 1 1B B B 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 1 与0“与”屏蔽,与1“与”保留
绘制流程图: 开始 7000H内容送A 得34H 高低位交换 屏蔽高4位后送7001H 得03H 7000H内容送A 得34H 屏蔽高位后送7002H 得04H 结束
;(DPTR)=7000H ;(A)=((DPTR))=(7000H)=34H 编写源程序:假设(7000H)=34H ORG 0050H CWORD: MOV DPTR,#7000H MOVX A,@DPTR MOV B,A SWAP A ANL A,#0FH INC DPTR MOVX @DPTR,A MOV A,B ANL A,#0FH INC DPTR MOVX @DPTR,A CWORD1:SJMP CWORD1 END
60KB 128B FFH FFFFH FFFFH FFH FFFFH 外部 特殊功能寄存 存储器 RAM SFR ) 器 ( 外部 52 子系列 ( ) 2000 H 80 H 80 H 1 FFFH 7 FH 数据缓冲器 30 H 外部 子系列 52 2 FH 内部 ROM 位寻址区 1000 H 20 H 128 ( 位 ) 0 FFFH 1 FH 128B 内部 工作寄存器区 0000 H 0000 H 00 H 0000 H EA = 1 EA = 0 4KB 程序存储器 内部数据存储器 外部数据存储器 64KB 128B+128B 64KB
;(DPTR)=7000H ;(A)=((DPTR))=(7000H)=34H 编写源程序: ORG 0050H CWORD: MOV DPTR,#7000H MOVX A,@DPTR MOV B,A SWAP A ANL A,#0FH INC DPTR MOVX @DPTR,A MOV A,B ANL A,#0FH INC DPTR MOVX @DPTR,A CWORD1:SJMP CWORD1 END ;(B)=34H, 送B保存,以备后用 ;高低4位交换,(A)=43H 高低四位交换 屏蔽高4位后送7001H ;(A)=03H ;(DPTR)=7001H 则(7001H)=03H ;((DPTR))=(7001H)=(A)=03H ; (A)=(B)=34H,B中34H送A ;(A)=04H 屏蔽高4位后送7002H ;(DPTR)=7002H 则(7002H)=04H ;((DPTR))=(7002H)=(A)=04H
实验三:拼字实验 要求:把7000H、7001H单元内容的低4位相拼后的字节送7002H。用于将显示缓冲区的数据取出拼装成一个字节。 分析思路,绘制流程图:问:怎么拼? 开始 7000H内容送A,屏蔽高4位 得03H 高低4位交换后送B 得30H 7001H内容送A,屏蔽高4位 得04H A和B相或后送7002H 得34H 结束
2、分支结构 通常用条件转移指令来实现分支结构
例:已知片内RAM的31H和32H单元中各有一个无符号例:已知片内RAM的31H和32H单元中各有一个无符号 数,试编程比较它们的大小,并将大数送到R0单元。 怎样比较(31H)、(32H)的大小呢? 利用两数相减后,判断CY: 若CY=0,则(31H)大;若CY=1,则(32H)大。 解:源程序如下: CLR C MOV A,31H SUBB A,32H LOOP:JNC LOOP1 MOV R0,32H SJMP LOOP2 LOOP1:MOV R0,31H LOOP2:LJMP $ ;(31H) →(A) ;(31H)≠(32H)转LOOP CJNE A,32H,LOOP ;(31H)-(32H)-(CY) →(A) ;CY=0,即(31H)>(32H)转LOOP1 ;CY=1,即(31H)<(32H)时,(32H)→(R0) ;(31H) →(R0)
3 循环结构 P72 一般情况下,循环程序应包括以下几个部分: (1) 循环初值:包括循环次数、循环体内工作单元的初值等。 (2) 循环体:需要多次执行的程序段。 (3)循环控制:用于判断是否结束循环。判断条件有两种方式: ◆ 用户设置固定的循环次数,利用DJNZ指令对循环次数进行计数,并判断是否结束循环。 ◆ 用户无法设置固定的循环次数,常用CJNE指令来实现循环结束条件判断。
置初值 置初值 循环体 Y 循环控制完 ? 退出循环 循环修改 N N 循环体 循环控制完 ? Y 循环修改 退出循环 (b)先判断再执行 不知循环次数,CJNE (a)先执行再判断 已知循环次数,DJNZ 图4.4 循环方式流程图 P73
实验一 • 试编程将7000H到70FFH存储单元清零 • 【编程思路】 • 存储单元清零可以直接采用数据传送类指令,直接将00H赋值给7000H到70FFH即可实现,但是需要清零的存储单元较多时,采用这种顺序结构的方式就比较繁琐,应采用循环结构。
绘制流程图 开始 设置起始地址,即7000H放于DPTR 获得循环次数即需清0的单元个数100H,放于R0 当前存储单元清0 指向下一个单元,且(R0)-1 是否已全部清0 即(R0)=0? 否 是 结束
编写源程序 ORG 0000H LJMP MAIN ORG 0030H MAIN: MOV DPTR, #7000H ;初始地址7000H ;放于DPTR MOV R0, #00H ;需清0的单元个数100H, ;放于R0 CLR A ;(A)=0 LOOP: MOVX @DPTR, A ;当前内容清0 INC DPTR ;指向下一个地址 DJNZ R0, LOOP ;是否已全部清0,(R0)-1=0? SJMP $ END (7001H)=0 (7000H)=0 ((DPTR))←(A)=0 7001H
操作 入口地址 0000 H 复位 外部中断 INT0 0003 H 000 BH 定时器 / 记数器 0 溢出 外部中断 INT 1 0013 H 001 BH 定时器 / 记数器 1 溢出 0023 H 串行口中断 定时器 / 记数器 2 溢出或 T 2 EX 端负跳变 ( MCS - 52 子系列 ) 002 BH MCS—51单片机复位和中断入口地址 P26
4.2.4 子程序设计和子程序调用 主程序 ┇ MOV A,3AH LCALL DELAY MOV B,A ┇ LCALL DELAY MOV A,B ┇ DELAY子程序 DELAY:MOV BL,AL ┇ ┇ RET
在编程时,使用子程序设计应注意以下几点: 1 要给每个子程序赋一个名字,它是子程序第一条指令前的标号。 2 调用子程序是通过调用指令来实现的,如LCALL子程序名。子程序的最后一条指令必须是返回指令(RET)。 3 参数传递问题。子程序应注意它的入口参数和出口参数,入口参数是由主程序传给子程序的参数,出口参数是子程序运算完传给主程序的运算结果。即在调用子程序时,若需要入口参数,必须对子程序的入口参数赋值。
参数传递一般采用以下方法: ◆ 利用寄存器或片内RAM传递子程序的参数, 这种方法常用 ◆ 若参数为位变量,则利用位地址传送子程序参数。 4 现场保护问题:若主程序和子程序中都使用了累加器和寄存器,一般采用堆栈方式保护和恢复这些数据。
例4-16: 设计 100 ms延时子程序,并通过调用子程序方式实现1s的延时子程序。P78 【编程思路】 1 由于单片机广泛应用在工控领域,所以延时程序经常被使用,单片机的延时程序一般有两种:指令延时(软件延时)和定时器延时(硬件延时)。定时器延时在后面的章节讲述,本例用指令延时方式。 2 单片机执行一条指令需要一定的时间, 利用单片机执行程序所用的时间来实现延时, 这种程序称为指令延时程序。如当系统使用12MHz晶振时,一个机器周期为1μs,执行一条双字节双周期DJNZ指令的时间为 2μs,因此 执行该指令50 000 次,就可以达到延时100ms的目的。 3 对于50 000次循环可采用内循环、外循环嵌套的多重循环结构。 250×200
分析思路,绘制流程图 设置外循环次数200 设置内循环次数250 执行内循环DJNZ,内循环次数-1 否 内循环结束? 是 执行外循环DJNZ,外循环次数-1 否 外循环结束? 是 结束
【100 ms延时子程序如下】 DELAY: MOV R6, #0C8H ; 外循环 200 次 LOOP1: MOV R7, #0F8H ; 内循环 248 次 NOP ; 时间补偿 LOOP2: DJNZ R7, LOOP2 ; 延时2 μs×248=496 μs DJNZ R6, LOOP1 ; 延时 500 μs×200=100 ms RET 红方框执行一次用时500us 1μs+(1+1+496+2)μs×200=100001us=100.001ms 1μs 1μs 1μs 496μs 2μs 【延时1s调用100ms延时子程序如下】 ;1s是100ms的10倍,即连续调用100ms延时子程序10次即可 DELAY1S: MOV R0, #10 LOOP: LCALL DELAY DJNZ R0, LOOP RET
实验一安排 实验名: 清零、拆字实验 时间: 第7周周二3、4节(10月18日) 地点: 信息工程系实验楼533