【计算机与UNIX汇编原理⑧】——UNIX编程进阶【分支程序、循环程序、子程序、宏指令】




编程时刻!⌨️

上一篇文章链接: 【计算机与UNIX汇编原理⑦】——DOS/BIOS功能调用【实现两数相加 + Hello World!】.
下一篇文章链接: 【计算机与UNIX汇编原理⑨】——实验报告一【题目:排除语法错误、统计数字】.


一、汇编程序概述

  ● 汇编语言编程属结构化程序设计。

  ● 汇编程序一般采用自上而下的设计方法,步骤为
    ① 分析题意,确定算法
    ② 根据算法画出程序框图
    ③ 根据框图编写程序
    ④ 上机调试程序
    ⑤ 执行程序

  ● 从结构上来讲,一般的汇编程序可分为
    ① 顺序程序
    ② 分支程序
    ③ 循环程序
    ④ 子程序
    ⑥ 宏指令

  ● 转移指令可用来实现分支程序设计,分支程序分三种
    ① 简单分支
    ② 复合分支
    ③ 多分支



二、顺序程序设计

  ● 和 C 语言的顺序结构一样。



三、分支程序设计

  ● 这里我们举两个例子来熟悉。

  ● 样例一【简单分支】:二进制数显示将 BX 寄存器中的内容 (5678H) 以二进制数格式 → 屏显

  ● 程序框图
在这里插入图片描述

  ● 代码如下

.586

CODE SEGMENT USE16
	 ASSUME	 CS:CODE		
	 
BEG: MOV	BX, 5678H		; 装入要显示的内容
	 MOV	CX, 16			; 循环次数:因为要显示 16 次二进制码

LAST:MOV	AL, '0'			; 0 的 ASCII 码
	 ROL 	BX, 1			; (不含进位的循环)左移
	 JNC 	NEXT			; 若当前 C 标志为 0, 则进行转移
	 MOV 	AL, '1'			; 1 的 ASCII 码
	
NEXT:MOV 	AH, 0EH			; 功能号0EH(BIOS):显示
	 INT 	10H
	 
	 LOOP 	LAST			; CX 将执行减 1 操作, 小于 0 时跳出循环
	 
	 MOV 	AH, 4CH			; 功能号4CH(DOS)
	 INT 	21H
	 
CODE ENDS
END BEG

  ◆ 代码说明:因为该程序不涉及内存中的数据,所以不用定义数据段,只定义了代码段。

  ● 运行结果

在这里插入图片描述
  ◆ 结果说明:“0101011001111000” → “0101 0110 0111 1000” → “5 6 7 8”


  ● 样例二【复合分支】:某科室9人,统计月收入在 800~900 间的人数,并用十进制数显示。

  ● 代码如下

.586

DATA	SEGMENT USE16
		NUM	DW	666, 930, 810, 850, 800, 900, 1000, 2000, 600	
DATA	ENDS

CODE 	SEGMENT	USE16
		ASSUME 	CS:CODE, DS:DATA
		
BEG:	MOV 	AX, DATA
		MOV 	DS, AX
		MOV 	BX, OFFSET 	NUM
		MOV		CL, 9					; 循环 9 次
		MOV 	DL, 0					; 记录 800~900 之间的个数
		
LAST:	CMP		WORD PTR [BX], 800
		JC 		NEXT					; < 800 时转
		CMP 	WORD PTR [BX], 900
		JA 		NEXT					; > 900 时转
		INC 	DL
		
NEXT: 	INC		BX			; 每个数占两个字节的单元, 所以需要自增两次
		INC		BX
		DEC 	CL			
		JNZ		LAST
		ADD		DL, 30H		; 将 DL 中存储的数字转化为 ASCII 码
		
		MOV 	AH, 2		; 功能号02H(显示)
		INT 	21H
		
		MOV		AH, 4CH
		INT 	21H
		
CODE 	ENDS
END 	BEG

  ● 运行结果

在这里插入图片描述

  ◆ 结果说明
    ① 810、850、800、900 这 4 个数字符合条件,所以答案是 4 。
    ② 月收入是无符号数,对一批数处理应该用间址。



四、循环程序设计

1、循环程序的组成

  ● 循环初始化部分:进入循环操作之前的准备工作。比如循环计数器的设置、缓冲区偏移地址的设置。通常使用寄存器或者内存单元作为循环计数器。

  ● 循环体:重复执行的程序代码。它是循环程序的核心部分。它会完成具体的、重复执行的工作以及为进入下次循环而进行的调整工作,如对循环次数的修改,缓冲区偏移地址的修改。

  ● 循环控制部分:判断循环条件是否成立。如果未满足,则继续循环,否则退出循环。


2、循环程序的结构

  ● 循环程序的结构一般有两种:单循环结构、双循环结构。
在这里插入图片描述


3、样例

  ● 【找出最大数】:设从 BUF 单元开始,存有若干字节的 ASCII 码,找出其中的最大数 → 屏幕显示。【注:ASCII 码为无符号数,无符号数的最小数为 0 】

  ● 程序框图

在这里插入图片描述

  ● 代码如下

.586

DATA 	SEGMENT USE16
		BUF DB 'ABCD123EFG'
		COUNT EQU $-BUF			; 统计串的长度
		MAX DB 'MAX=', ?, 0DH, 0AH, '$'		; 格式化输出串
DATA 	ENDS

CODE 	SEGMENT USE16
		ASSUME CS:CODE, DS:DATA
		
BEG:	MOV 	AX, DATA
		MOV 	DS, AX
		MOV 	AL, 0			; 先将无符号最小数 0 → AL
		LEA		BX, BUF			; 串首址的偏移量 → BX
		MOV 	CX, COUNT		; 串长度 → CX
		
LAST: 	CMP 	[BX], AL		
		JC 		NEXT
		MOV		AL, [BX]		; 将最大数 → AL

NEXT: 	INC 	BX
		LOOP 	LAST			; CX-1 → CX, 结果不为 0, 进行转移
		
		MOV 	MAX+4, AL		; 再将最大数 → 变量 "MAX的 ? 中去"
		
		MOV 	AH, 9			; 显示
		MOV 	DX, OFFSET MAX
		INT		21H
		
		MOV 	AH, 4CH
		INT 	21H
		
CODE 	ENDS
END BEG

  ● 运行结果

在这里插入图片描述



五、子程序设计

  ● 子程序是相对独立的程序。当程序中要多次完成某一操作时,为了简化整体程序,增强程序可读性。常常把 “完成某一操作” 设计成一个子程序,供主程序调用。【注:在汇编语言中,子程序也称为过程】

在这里插入图片描述
  ● 子程序设计规则
    ① 子程序用 PROC/ENDP 定界。
    ② 子程序分为:段内子程序、段间子程序、无参数子程序有参数子程序
    ③ 向子程序传递参数的方法:通过寄存器传递参数、利用堆栈区传递参数、利用内存单元传递参数。

"无参数子程序的模板:"
CODE SEGMENT
...
CALL CRLF
...
CALL CRLF
...

"举例:无参数子程序"
CRLF PROC

	MOV AH, 2 
	MOV DL, 0DH 		; 回车(CR)
	INT 21H
	
	MOV AH, 2
	MOV DL, 0AH  		; 换行(LF)
	INT 21H
	
	RET
CRLF ENDP

  ◆ 说明:
    回车符:将光标带到行的开头,(carriage return)
    换行符:将光标向下移动一个空格(line feed)


"例:用寄存器传递参数"
MESG1 DB 'Please Enter 0~9:$'			  ; 数据段
MESG2 DB 'Strike any key', 0DH, 0AH,'$'

CODE SEGMENT				; 代码段
...
MOV DX,OFFSET MESG1
CALL DISP
...
MOV DX,OFFSET MESG2
CALL DISP
...

;----名为 DISP 的子程序--------

DISP PROC
	MOV AH, 9
	INT 21H
	RET
DISP ENDP
CODE ENDS


六、宏指令与条件汇编

  ● 子程序调用可以简化程序,但是如果子程序的参数太多,调用时太麻烦,汇编语言提供了另一种简化的途径——宏指令。
  ● “宏” 在计算机领域里的一般概念:将一系列操作组织成为一个整体的功能。【例:Windows Shell中的宏操作】

  ● 宏指令的补充说明
    ① 宏指令是汇编语言提供的伪指令,它是用户自行定义的若干指令的集合。
    ② 宏指令有:无参数宏指令、有参数宏指令。
    ③ 宏指令应该先定义后调用
    ④ 宏指令的定义,可以不在任何逻辑段之中,习惯上放在源程序首部。


1、无参数宏指令的定义与调用

  ■ 定义格式

宏指令名字 MACRO
	 ...宏体...
		 ENDM

  ■ 宏指令调用格式:宏指令先行定义之后,在代码段的任何位置,书写 “宏指令名字” 即完成了调用。【本质上就是一种 “替换” 】

  ● 宏指令的汇编过程:汇编时,汇编程序自动用宏体取代宏调用(即宏指令名字所在处)。

"举例"
.586
CRLF MACRO 			; 实现回车换行的 “宏”
	MOV AH,0EH
	MOV AL,0DH
	INT 10H
	MOV AL,0AH
	INT 10H
ENDM

;-------------------
CODE	SEGMENT USE16
	 	ASSUME CS:CODE
	 	
BEG: 	MOV AH,2
		MOV DL,'A'
		INT 21H
		CRLF		; 汇编时, 自动用宏体替换宏指令, 将其拷贝一份到这里


2、有参数的宏指令及其调用

  ■ 定义格式

宏指令名字 MACRO 用逗号(或空格)间隔的哑元表
		宏体
		ENDM

  ■ 调用格式:宏指令名字 实元表

  ◆ 注意:实元表是一串用逗号(或空格)间隔的立即数,寄存器操作数或者是没有 PTR 说明符的存储器操作数。汇编时,汇编程序自动地把实元一一对应的赋给哑元


3、LOCAL伪指令

  ● 若宏体中有分支、转移,则必然有标号。若调用两次以上这种宏体,会出现标号重复定义的错误,为此汇编语言提供了 LOCAL 伪指令。

  ■ 格式:LOCAL 用逗号间隔的标号名

  ● 应用
    ① 用 LOCAL 定义的标号称为 “局部标号” ,它是宏体中所有标号的集合。局部标号可以和代码段中其他标号重名。
    ② 宏指令仅被调用 1 次时,不必使用 LOCAL 伪指令。

"例: 分组显示 8、16、32 位二进制数, 并将变量 NUM 的数据以二进制的形式输出:"
.586

CRLF MACRO 			; 光标返回下一行始格
	MOV AH, 0EH
	MOV AL, 0DH 
	INT 10H
	MOV AL, 0AH
	INT 10H
ENDM
;------------------
DISP MACRO VAR, NN 		; 分组显示 VAR 中的 NN 位二进制数
	LOCAL L1, L2		; LOCAL 伪指令
	MOV CH, NN/4		; 得到分组数 → CH
	
L1: MOV CL, 4
L2: MOV AL, '0'

	ROL VAR, 1			; 显示 1 位二进制数
	ADC AL, 0
	MOV AH, 0EH
	INT 10H
	DEC CL			
	JNZ L2
	
	MOV AL, ' '			; 显示 “间隔”
	INT 10H
	
	DEC CH
	JNZ L1
	CRLF 
	ENDM
;------------------
CODE	SEGMENT USE16
		NUM DB 88H
		ASSUME CS:CODE
BEG: 	MOV EBX, 12345678H 
		DISP BH, 8 			; 显示 BH 中的 8 位数
		DISP BX, 16 		; 显示 BX 中的 16 位数
		DISP EBX, 32 		; 显示 EBX 中的 32 位数
		DISP NUM, 8			; 显示 NUM 单元中的 8 位数


		MOV AH, 4CH
		INT 21H
CODE ENDS
END BEG

  ● 运行结果

在这里插入图片描述


4、宏指令和子程序调用的相同点和不同点

  ● 相同点:都可以简化程序设计。在调用发生1次以上时,都可以减小源程序的尺寸。


  ● 不同点
    ① 调用发生的时刻不同:宏调用在程序汇编时刻发生,由汇编程序完成调用功能;子程序调用在程序运行时刻发生,由CPU执行调用指令并完成调用功能。宏调用对目标程序的尺寸减小无任何贡献;而子程序调用对目标程序的尺寸减小有贡献(当调用发生1次以上时)。

    ② 调用需要的时间不同:宏调用是替换操作,需要的时间相对短;子程序调用是程序控制流转移即由主程序 ——> 子程序 ——> 主程序的过程,并且使用堆栈保存断点地址,需要的时间相对长。

    ③ 调用的方式和参数传递方式不同:宏调用使用宏的名称即可,子程序调用需要 CALL 以及 RET 指令;宏调用通过形参和实参一一对应实现参数传递,子程序调用通过寄存器,堆栈以及内存单元传递参数。



七、参考附录:

[1] 《微型计算机原理与接口技术(慕课板)》
清华大学出版社

[2] 《汇编语言程序设计(第2版)》

上一篇文章链接: 【计算机与UNIX汇编原理⑦】——DOS/BIOS功能调用【实现两数相加 + Hello World!】.

下一篇文章链接: 【计算机与UNIX汇编原理⑨】——实验报告一【题目:排除语法错误、统计数字】.


⭐️ ⭐️

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一支王同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值