(全网最全)微型计算机原理与接口技术第六版第4章课后习题答案-周荷琴,冯焕清-中国科学技术大学出版社

被骂了,我一个都快毕业的大四回来做大三的课
1.
从编写汇编语言源程序到生成可执行文件.EXE,需要经过哪些步骤?
解:
见书本P117页

首先,用编辑程序EDIT将用户编写的汇编语言程序键入计算机,按照规定格式编排后,以文件的形式存放在磁盘上。文件由文件名和扩展名两部分组成,扩展名前面要加一个圆点“.”,汇编过程中使用的文件名可以由用户自行定义,这里假设文件名为PROG。扩展名是由系统规定的,汇编语言源程序、目标文件和可执行文件的扩展名分别为.ASM、.OBJ和.EXE。
汇编语言程序经编辑程序EDIT编排后生成汇编语言源程序PROG.ASM,经汇编程序MASM汇编后生成目标文件PROG.OBJ,此时还可以生成一个可选的列表文件PROG.LIST,目标文件经连接程序LINK连接后,生成可执行文件PROG.EXE。
2. 指令语句和伪指令语句各由哪几个字段组成?哪些字段是必不可少的?
指令语句的组成字段:
指令语句由以下4个字段组成:
- 标号:表示指令的符号地址,后跟冒号“:”。(可选)
- 指令助记符:表示指令的操作码。(必不可少)
- 操作数:指令的操作对象,可以是常数、变量、寄存器等。(根据指令需求,可能没有、一个或多个)
- 注释:用来说明指令功能,以分号“;”开头。(可选)
必不可少的字段:指令助记符。
伪指令语句的组成字段:
伪指令语句由以下4个字段组成:
- 名字:伪指令语句的名称,符号地址表示。(可选,但某些伪指令必须)
- 伪指令指示符:MASM规定的伪指令符号(如SEGMENT、DB等)。(必不可少)
- 操作数:根据伪指令要求确定,可能没有、一个或多个。(根据伪指令需求)
- 注释:说明伪指令功能,以分号“;”开头。(可选)
必不可少的字段:伪指令指示符。
3. 伪指令语句的作用是什么?它与指令语句的主要区别是什么?
伪指令语句是指示性语句,没有对应的机器码,不直接由CPU执行,而是在汇编过程中由汇编程序处理,用于完成以下功能:
- 定义程序结构(如段定义SEGMENT/ENDS、过程定义PROC/ENDP)。
- 分配存储空间或初始化数据(如DB、DW、DD等)。
- 指定段与段寄存器的关系(如ASSUME)。
- 控制汇编过程(如程序结束END)。
与指令语句的主要区别:
- 功能不同:
- 指令语句:对应CPU可执行的机器指令,完成实际运算或操作(如MOV、ADD等)
- 伪指令语句:指导汇编程序如何汇编,不生成机器码。
- 执行时机:
- 指令语句:在程序运行时由CPU执行。
- 伪指令语句:在汇编阶段由汇编程序处理。
- 字段格式:
- 指令语句的标号后加冒号“:”,伪指令语句的名字后不加冒号。
- 指令语句的核心是指令助记符,伪指令语句的核心是伪指令指示符。
关键区别:伪指令是汇编时的控制命令,指令是运行时的CPU操作码。
4. 下列指令分别完成什么功能?
(1) MOV AL, NOT 10001110B
- 功能:将
10001110B(二进制)按位取反(NOT运算),并将结果存入AL寄存器。 - 计算过程:
10001110B(原值)=8Eh(十六进制)NOT 10001110B=01110001B(按位取反)=71h
- 结果:
AL = 71h(即01110001B)。
(2) MOV CX, 8 GT 00011000B
- 功能:比较
8和00011000B(即24),若8 > 24成立,则CX = 1(真),否则CX = 0(假)。 - 计算过程:
00011000B=24(十进制)8 > 24→ 不成立(0)
- 结果:
CX = 0(因为8不大于24)。
(3) MOV DL, 27/5
- 功能:计算
27 / 5的整数部分(汇编语言中除法默认取整),并将结果存入DL。 - 计算过程:
27 / 5 = 5(余数舍弃)
- 结果:
DL = 5。
(4) MOV BX, $-LIST
- 功能:计算当前指令的地址(
$)与符号LIST的地址之间的偏移量(字节数),并将结果存入BX。 - 说明:
$表示当前指令的地址(即MOV BX, $-LIST所在的地址)。LIST是程序中定义的某个标号或变量。$-LIST计算的是从LIST到当前指令的字节距离(通常用于计算数组长度或缓冲区大小)。
- 示例(假设
LIST的地址比$小10字节):- 结果:
BX = 10(即LIST到当前指令的偏移量)。
- 结果:
5. 阅读下列程序段,说明每条指令执行后的结果是什么。
```asm
X1 DB 65H,78H
X2 DW 06FFH,5200H
X3 DD ?
GO: MOV AL, TYPE X1
MOV BL, TYPE X2
MOV CL, TYPE X3
MOV AH, TYPE GO
MOV BH, SIZE X2
MOV CH, LENGTH X3
```
解:
X1 DB 65H,78H ; 定义字节变量X1(占2字节)
X2 DW 06FFH,5200H ; 定义字变量X2(占4字节)
X3 DD ? ; 定义双字变量X3(占4字节)
GO: MOV AL, TYPE X1 ; AL = 1 (DB类型大小为1字节)
MOV BL, TYPE X2 ; BL = 2 (DW类型大小为2字节)
MOV CL, TYPE X3 ; CL = 4 (DD类型大小为4字节)
MOV AH, TYPE GO ; AH = 0FFH (标号GO的NEAR类型值为-1,截取低8位)
MOV BH, SIZE X2 ; BH = 2 (单个字元素大小为2字节)
MOV CH, LENGTH X3 ; CH = 1 (单个双字元素计数为1)
详细说明:
-
MOV AL, TYPE X1TYPE运算符返回变量的字节大小X1是DB(字节)类型 → 大小 = 1 字节- 结果:
AL = 01H
-
MOV BL, TYPE X2X2是DW(字)类型 → 大小 = 2 字节- 结果:
BL = 02H
-
MOV CL, TYPE X3X3是DD(双字)类型 → 大小 = 4 字节- 结果:
CL = 04H
-
MOV AH, TYPE GOTYPE对标号返回类型值:NEAR = -1(0FFFFH),FAR = -2(0FFFEH)GO是默认的 NEAR 标号 → 类型值 = 0FFFFH- 截取低8位 → 0FFH(因为AH是8位寄存器)
- 结果:
AH = 0FFH
-
MOV BH, SIZE X2SIZE=LENGTH×TYPEX2无DUP定义 →LENGTH = 1TYPE X2 = 2→SIZE = 1 × 2 = 2- 结果:
BH = 02H
-
MOV CH, LENGTH X3LENGTH返回元素数量(无DUP时均为1)X3定义为单个双字元素 → 计数 = 1- 结果:
CH = 01H
6.
画出示意图,说明下列变量在内存中如何存放。
```asm
A1 DB 12H,34H
A2 DB 'Right.'
A3 DW 5678H
A4 DB 3 DUP (?)
```
| 内存地址 | 存储内容(十六进制) | 说明 | 对应变量 |
|---|---|---|---|
| 0000H | 12H |
第一个字节数据 | A1[0] |
| 0001H | 34H |
第二个字节数据 | A1[1] |
| 0002H | 52H |
'R' 的 ASCII 码 | A2[0] |
| 0003H | 69H |
'i' 的 ASCII 码 | A2[1] |
| 0004H | 67H |
'g' 的 ASCII 码 | A2[2] |
| 0005H | 68H |
'h' 的 ASCII 码 | A2[3] |
| 0006H | 74H |
't' 的 ASCII 码 | A2[4] |
| 0007H | 2EH |
'.' 的 ASCII 码 | A2[5] |
| 0008H | 78H |
字的低字节 (78H) | A3[0] |
| 0009H | 56H |
字的高字节 (56H) | A3[1] |
| 000AH | ?? |
未初始化字节 | A4[0] |
| 000BH | ?? |
未初始化字节 | A4[1] |
| 000CH | ?? |
未初始化字节 | A4[2] |

7. 给出完整的汇编语言程序设计框架,并说明其中每条伪指令语句的功能。
解:
框架代码见书本P128页-4.1.3完整的汇编语言程序框架

伪指令功能说明
-
SEGMENT/ENDS:- 功能:定义逻辑段(数据段/附加段/堆栈段/代码段)
- 说明:成对使用,
SEGMENT标记段开始,ENDS标记段结束 - 示例:
DATA SEGMENT定义数据段开始,DATA ENDS定义结束
-
DB/DW:- 功能:定义变量并分配存储空间
DB:定义字节类型(8位)DW:定义字类型(16位)- 示例:
X DB ?分配1字节未初始化空间
-
DUP(?):- 功能:重复分配未初始化空间
- 示例:
DB 100 DUP(?)分配100字节未初始化空间
-
EQU:- 功能:定义符号常量(不分配空间)
- 示例:
TOP EQU LENGTH STAPN定义TOP为STAPN的长度值
-
PROC/ENDP:- 功能:定义过程(子程序)
FAR:指定为远过程(可跨段调用)- 示例:
MAIN PROC FAR定义主过程开始,MAIN ENDP结束定义
-
ASSUME:- 功能:指定段寄存器与逻辑段的关联关系
- 说明:仅告知汇编程序,不实际加载段寄存器
- 示例:
ASSUME CS:CODE指定CS指向代码段
-
END:- 功能:标记源程序结束并指定程序入口点
- 说明:MASM 6.0+要求指定入口标号(如
END MAIN) - 作用:结束汇编并设置执行起始地址
8. 从汇编语言程序返回 DOS,有哪几种方法?哪一种是最常用的方法?
解:见书本130页

三种返回 DOS 的方法:
1. 程序框架设定的远过程方法
实现方式:
PUSH DS ; 将DS入栈
SUB AX, AX ; AX清零
PUSH AX ; 00H入栈(偏移地址)
RET ; 远返回
原理:构造远返回地址(DS:0000H),执行RET后跳转到DOS的终止处理程序(INT 20H)
特点:需将主程序定义为FAR过程,是传统方法但不够灵活
2.DOS 功能调用(4CH号)
实现方式:
MOV AX, 4C00H ; AH=4CH(程序终止功能),AL=00H(返回码)
INT 21H ; 调用DOS中断
原理:通过INT 21H调用DOS的终止程序功能
特点:
自动关闭所有打开的文件,防止数据丢失
释放程序占用的内存
无需定义过程结构(可直接在代码段中使用)
支持返回码传递(通过AL寄存器)
3.INT 20H 指令(仅限.COM文件)
实现方式:
Asm
INT 20H ; 直接调用DOS终止中断
限制:仅适用于.COM格式程序(.EXE程序使用会导致崩溃)
最常用方法:DOS 功能调用(4CH号)
推荐理由:
功能最完整:自动处理文件关闭和内存释放
安全性最高:避免数据丢失风险
兼容性最好:适用于所有程序格式(.EXE/.COM)
灵活性最强:可直接插入代码任何位置,无需特定程序结构
现代编程标准:教材和实践中广泛推荐
9. DOS 功能调用和 BIOS 中断调用各分哪几个步骤进行?
解:
DOS功能调用步骤(使用INT 21H)
-
设置功能号:将功能号(00H~6CH)送入AH寄存器
-
设置入口参数:根据功能要求设置相关寄存器
-
执行中断指令:调用INT 21H
-
处理出口参数:从指定寄存器获取返回结果
示例:
MOV AH, 02H ; 功能号02H(显示字符)
MOV DL, 'A' ; 入口参数
INT 21H ; 执行调用
; 出口:屏幕显示'A'
BIOS中断调用步骤(使用INT n)
-
设置功能号:将子功能号送入AH寄存器
-
设置入口参数:初始化相关寄存器
-
执行指定中断:调用对应的INT n指令
-
分析出口状态:检查返回寄存器和状态标志
示例(读取键盘状态):
MOV AH, 02H ; 功能号02H
INT 16H ; 调用BIOS键盘服务
; 出口:AL=键盘状态字节
10.
编写汇编语言程序段,完成如下功能:
(1)从键盘输入一个字符串“Please input a number:”,存入 BUFF 开始的内存单元中。
(2)把内存中从 BUFF 单元开始存放的字符串显示在屏幕上。
(分别参考例 4.21 和例 4.23。)
;====== 数据段定义 ======
DATA SEGMENT
BUFF DB 50 ; 最大输入长度 (50字节)
DB ? ; 实际输入长度存储位置
DB 50 DUP(?) ; 输入字符串存储空间
DATA ENDS
;====== 代码段定义 ======
CODE SEGMENT
ASSUME CS:CODE, DS:DATA
MOV AX, DATA ; 初始化DS寄存器
MOV DS, AX
;==== 0AH号功能调用:输入字符串 ====
MOV DX, OFFSET BUFF ; DS:DX指向缓冲区
MOV AH, 0AH ; 功能号0AH
INT 21H ; 调用DOS中断
;==== 添加字符串结束标记 ====
MOV BL, BUFF + 1 ; 获取实际输入长度
MOV BH, 0 ; 高位清零
MOV BYTE PTR [BUFF + 2 + BX], '$' ; 在字符串末尾添加$
;==== 09H号功能调用:显示字符串 ====
MOV DX, OFFSET BUFF + 2 ; DS:DX指向字符串起始位置
MOV AH, 09H ; 功能号09H
INT 21H ; 调用DOS中断
;==== 程序结束返回DOS ====
MOV AX, 4C00H ; 4CH功能调用
INT 21H
CODE ENDS
END
11. 编程实现:在显示器上显示全部标准和扩展 ASCII 码(其编码为 00~FFH)字符。(参考例 4.33。)
解:
; 程序描述:显示全部标准和扩展ASCII字符 (00H-FFH)
; 程序名称:SHOWASCII.ASM
; 数据段定义
DATA SEGMENT
NEWLINE DB 0DH, 0AH, '$' ; 回车换行符
COUNTER DB 0 ; 计数器(每行显示16个字符)
DATA ENDS
; 代码段定义
CODE SEGMENT
ASSUME CS:CODE, DS:DATA
START:
MOV AX, DATA ; 初始化数据段寄存器
MOV DS, AX
MOV BL, 0 ; BL = 当前ASCII码 (00H)
MOV CX, 256 ; 循环256次 (00H-FFH)
DISPLAY_LOOP:
; 显示当前字符
MOV DL, BL ; 将ASCII码送入DL
MOV AH, 02H ; DOS显示字符功能
INT 21H
; 增加计数器并检查是否需要换行
INC COUNTER
CMP COUNTER, 16 ; 每行显示16个字符
JB NO_NEWLINE
; 显示换行
LEA DX, NEWLINE ; 加载换行符地址
MOV AH, 09H ; DOS显示字符串功能
INT 21H
MOV COUNTER, 0 ; 重置计数器
NO_NEWLINE:
INC BL ; 移动到下一个ASCII码
LOOP DISPLAY_LOOP ; 继续循环
; 程序结束
MOV AH, 4CH ; DOS程序终止功能
INT 21H
CODE ENDS
END START
输出结果:

12. 编程实现:从键盘输入一个十进制数字 0~9,查表求键入数字的七段代码,存入 DL 中,并在键入数字之前,显示提示信息“Please input a number:”。(参考例 4.34。)
解: 七段代码值自己虚拟,下面只给出一个假想的TABLE例子,真实中需要结合开发板指定(实际硬件连接不同,代码可能不同,这里给出一个示例表)
; 定义数据段
DATA SEGMENT
; 0-9的七段代码表 (共阴极数码管)
; 格式: g f e d c b a (高位到低位)
TABLE DB 3FH ; 0: 0011 1111
DB 06H ; 1: 0000 0110
DB 5BH ; 2: 0101 1011
DB 4FH ; 3: 0100 1111
DB 66H ; 4: 0110 0110
DB 6DH ; 5: 0110 1101
DB 7DH ; 6: 0111 1101
DB 07H ; 7: 0000 0111
DB 7FH ; 8: 0111 1111
DB 6FH ; 9: 0110 1111
PROMPT DB 'Please input a number (0-9): $' ; 提示信息
DATA ENDS
; 定义代码段
CODE SEGMENT
ASSUME CS:CODE, DS:DATA
START:
; 设置数据段寄存器
MOV AX, DATA
MOV DS, AX
; 显示提示信息
MOV AH, 09H ; DOS显示字符串功能
MOV DX, OFFSET PROMPT
INT 21H
; 等待键盘输入
MOV AH, 01H ; DOS字符输入功能
INT 21H ; AL = 输入字符的ASCII码
; 检查输入是否为数字(0-9)
CMP AL, '0'
JB INVALID ; 小于'0'无效
CMP AL, '9'
JA INVALID ; 大于'9'无效
; 转换ASCII数字为二进制值
SUB AL, '0' ; AL = 0-9
; 查表获取七段代码
MOV BX, OFFSET TABLE; BX指向表头
XOR AH, AH ; 清除AH
ADD BX, AX ; 计算表项地址
MOV DL, [BX] ; DL = 七段代码
; 程序结束
JMP EXIT
INVALID:
; 无效输入处理
MOV DL, 00H ; 无效输入时设置DL为0
EXIT:
; 返回DOS
MOV AH, 4CH
INT 21H
CODE ENDS
END START
13. 某个学生的英语成绩已存放在 BL 中,如果低于 60 分,则显示 F(Fail),如高于或等于 85 分,则显示 G(Good),否则显示 P(Pass),试编写完整的汇编语言程序来实现。(程序流程见图 4.8。)
解:
; 定义堆栈段
STACK SEGMENT PARA STACK 'STACK'
DB 100 DUP(?) ; 分配100字节的堆栈空间
TOP LABEL WORD ; 定义堆栈顶部位置
STACK ENDS
; 定义代码段
CODE SEGMENT
ASSUME CS:CODE, SS:STACK
; 主程序
MAIN PROC FAR
; 设置堆栈
MOV AX, STACK
MOV SS, AX
MOV SP, OFFSET TOP ; 设置堆栈指针
; DOS程序返回设置
PUSH DS
XOR AX, AX
PUSH AX
; 英语成绩已存放在BL中(示例值)
MOV BL, 75 ; 测试成绩,可修改为任意值(0 - 100)
; 判断成绩等级
CMP BL, 60 ; 比较成绩与60分
JB FAIL ; <60分,转FAIL
CMP BL, 85 ; ≥60分,比较成绩与85分
JAE GOOD ; ≥85分,转GOOD
; 60 - 84分,显示P(PASS)
MOV DL, 'P' ; 准备显示字符'P'
JMP PRINT
FAIL:
; <60分,显示F(FAIL)
MOV DL, 'F' ; 准备显示字符'F'
JMP PRINT
GOOD:
; ≥85分,显示G(GOOD)
MOV DL, 'G' ; 准备显示字符'G'
PRINT:
; 显示字符
MOV AH, 02H ; DOS字符显示功能
INT 21H
; 程序结束
MOV AH, 4CH ; DOS程序结束功能
INT 21H
MAIN ENDP
CODE ENDS
END MAIN
14. 在 TABLE 开始的内存字节单元中,存放了 12 个带符号数,试编写完整的汇编语言程序统计其中的正数、负数和零的个数,分别存入 PLUS、NEG 和 ZERO 单元中。(参考例 3.89。)
解:
; 定义数据段
DATA SEGMENT
; 12个带符号数
TABLE DB 01H, 80H, 0F5H, 32H, 86H, 74H
DB 49H, 0AFH, 25H, 40H, 00H, 0FFH
PLUS DB 0 ; 存放正数个数,6个
NEGT DB 0 ; 存放负数个数,5个
ZERO DB 0 ; 存放零的个数,1个
DATA ENDS
; 定义堆栈段
STACK SEGMENT PARA STACK 'STACK'
STAPN DB 100 DUP(?) ; 分配100字节堆栈空间
TOP EQU LENGTH STAPN ; 定义堆栈顶部位置
STACK ENDS
; 定义代码段
CODE SEGMENT
ASSUME CS:CODE, DS:DATA, SS:STACK
; 主程序
MAIN PROC FAR
; 设置堆栈段
MOV AX, STACK
MOV SS, AX
MOV SP, TOP ; 设置堆栈指针
; DOS程序返回设置
PUSH DS
XOR AX, AX
PUSH AX
; 设置数据段
MOV AX, DATA
MOV DS, AX
; 初始化计数器
MOV CX, 12 ; 数据总数
MOV BX, 0 ; 数据指针(初始为0)
MOV PLUS, 0 ; 正数计数器清零
MOV NEGT, 0 ; 负数计数器清零
MOV ZERO, 0 ; 零计数器清零
AGAIN:
; 取一个数与0比较
CMP TABLE[BX], 0
; 大于等于0的处理
JGE GRET_EQ ; ≥0, 转GRET_EQ
; 小于0的处理(负数)
INC NEGT ; 负数个数加1
JMP NEXT ; 跳到下一步
GRET_EQ:
; 等于0还是大于0?
JG P_INC ; >0, 转P_INC
; 等于0的处理
INC ZERO ; 零个数加1
JMP NEXT ; 跳到下一步
P_INC:
; 大于0的处理(正数)
INC PLUS ; 正数个数加1
NEXT:
; 更新指针和计数器
INC BX ; 数据地址指针加1
DEC CX ; 数据计数器减1
JNZ AGAIN ; 未完,继续统计
; 程序结束
MOV AH, 4CH ; DOS程序结束功能
INT 21H
MAIN ENDP
CODE ENDS
END MAIN
内存DS段结果,076E:0000处在FFH之后可以看到06 05 01

15. 已知:在内存 BUFF 开始的单元中,存有一串字节数据:58、75、36、42、89,编程找出其中的最小值存入 MIN 单元中,并将这个数显示在屏幕上。(参考例 4.38。)
解:
方案一:(假设字节数据为十六进制)
; 定义堆栈段
STACK SEGMENT STACK
DB 100 DUP(?) ; 预留100字节的堆栈空间
STACK ENDS
; 定义数据段
DATA SEGMENT
BUF DB 58H, 75H, 36H, 42H, 89H ; 定义字节数组
MIN DB 0 ; 存储最小值的变量
DATA ENDS
; 定义代码段
CODE SEGMENT
ASSUME CS:CODE, DS:DATA, SS:STACK
START:
PUSH DS ; 保存DS寄存器
MOV AX, DATA ; 初始化数据段寄存器
MOV DS, AX
; 查找数组中的最小值
MOV CX, 4 ; 循环次数(数组长度-1)
MOV BX, OFFSET BUF ; 数组首地址
MOV AL, [BX] ; 取第一个元素作为初始最小值
ST1:
INC BX ; 指向下一个元素
CMP AL, [BX] ; 比较当前最小值与当前元素
JBE NEXT ; 如果 AL <= [BX],跳过更新
MOV AL, [BX] ; 否则更新最小值
NEXT:
LOOP ST1 ; 循环处理
MOV MIN, AL ; 保存最终的最小值
; 将最小值以ASCII形式输出(高4位)
AND AL, 0F0H ; 取高4位
MOV CL, 4
ROR AL, CL ; 右移4位(高4位移到低4位)
ADD AL, 30H ; 转换为ASCII
MOV DL, AL ; 准备输出
MOV AH, 02H
INT 21H ; 调用DOS显示字符
; 输出低4位
MOV AL, MIN
AND AL, 0FH ; 取低4位
ADD AL, 30H ; 转换为ASCII
MOV DL, AL
MOV AH, 02H
INT 21H
; 程序退出
POP DS ; 恢复DS寄存器
MOV AH, 4CH ; DOS退出功能号
INT 21H
HLT ; 暂停处理器
CODE ENDS
END START
方案二:下面的数组数据不考虑大于100的情况
; 定义堆栈段
STACK SEGMENT PARA STACK 'STACK'
STAPN DB 100 DUP(?) ; 分配100字节的堆栈空间
TOP EQU LENGTH STAPN ; 定义堆栈顶部位置
STACK ENDS
; 定义数据段
DATA SEGMENT
BUF DB 58, 75, 36, 42, 89 ; 给定的字节数据
;3AH, 4BH, 24H, 2AH, 59H
COUNT EQU ($ - BUF) ; 计算数据个数(字节数)
MIN DB ? ; 最小值存储单元
DATA ENDS
; 定义代码段
CODE SEGMENT
ASSUME CS:CODE, DS:DATA, SS:STACK
; 主程序
MAIN PROC FAR
; 初始化堆栈
MOV AX, STACK
MOV SS, AX
MOV SP, TOP ; 设置堆栈指针(假设TOP在堆栈段顶部)
; 保存返回地址
PUSH DS
XOR AX, AX
PUSH AX
; 设置数据段
MOV AX, DATA
MOV DS, AX
; 初始化最小值(取第一个元素)
LEA SI, BUF ; SI指向缓冲区首地址
MOV AL, [SI] ; AL = 第一个元素
MOV MIN, AL ; 暂存为最小值
; 遍历数组查找最小值
MOV CX, COUNT ; CX = 数据个数
DEC CX ; 需要比较的次数(n-1)
JCXZ EXIT ; 如果只有1个元素,直接退出
INC SI ; 指向下一个元素
SEARCH_LOOP:
MOV AL, MIN ; AL = 当前最小值
CMP AL, [SI] ; 与当前元素比较
JLE NEXT_ELEMENT ; 如果当前最小值 <= 元素,跳过
MOV AL, [SI] ; 否则更新最小值
MOV MIN, AL
NEXT_ELEMENT:
INC SI ; 指向下一个元素
LOOP SEARCH_LOOP ; 继续循环
EXIT:
; 显示最小值(0-99,不考虑100以上的情况)
MOV AL, MIN
XOR AH, AH ; AX = MIN(0-99),AX归零
; 处理十位和个位
MOV BL, 10
DIV BL ; AL = 十位,AH = 个位
MOV CL, AL ; 保存十位
MOV CH, AH ; 保存个位
; 显示十位(如果有)
CMP CL, 0
JE DISPLAY_UNIT ; 十位为0则跳过
MOV DL, CL
ADD DL, '0'
MOV AH, 02H
INT 21H
DISPLAY_UNIT:
; 显示个位
MOV DL, CH
ADD DL, '0'
MOV AH, 02H
INT 21H
; 程序结束
RET
MAIN ENDP
CODE ENDS
END MAIN
16.
内存中有一组无符号字节数据,要求编程按从小到大的顺序排列。(参考例 4.40。)
解:
; 定义数据段
DATA SEGMENT
ARRAY DB 12, 7, 19, 8, 24 ; 示例数据(可替换为任意无符号字节数据)
;十六进制依次为 0x0C, 0x07, 0x13, 0x08, 0x18
COUNT EQU $-ARRAY ; 数组长度(字节数)
DATA ENDS
; 定义堆栈段
STACK SEGMENT PARA STACK 'STACK'
STAPN DB 100 DUP(?) ; 分配100字节堆栈空间
TOP EQU LENGTH STAPN ; 定义堆栈顶部位置
STACK ENDS
; 定义代码段
CODE SEGMENT
ASSUME CS:CODE, DS:DATA, SS:STACK
; 主程序
MAIN PROC FAR
; 设置堆栈段
MOV AX, STACK
MOV SS, AX
; 设置堆栈指针(假设堆栈大小为100字节)
MOV SP, TOP
; DOS程序返回设置
PUSH DS
XOR AX, AX
PUSH AX
; 设置数据段
MOV AX, DATA
MOV DS, AX
; 冒泡排序算法实现(升序)
MOV CX, COUNT - 1 ; 外层循环次数(n-1)
OUTER_LOOP:
MOV DI, CX ; 保存外层循环计数器
MOV SI, 0 ; 数组索引(初始为0)
INNER_LOOP:
MOV AL, ARRAY[SI] ; 取当前元素
MOV BL, ARRAY[SI+1] ; 取下一个元素
; 比较相邻元素(无符号比较)
CMP AL, BL
JBE NO_SWAP ; AL <= BL? 是则跳过交换
; 交换元素(因为AL > BL)
MOV ARRAY[SI], BL ; 将较小的元素移到前面
MOV ARRAY[SI+1], AL ; 将较大的元素移到后面
NO_SWAP:
INC SI ; 移动到下一对元素
LOOP INNER_LOOP ; 继续内层循环
; 恢复外层循环计数器
MOV CX, DI
LOOP OUTER_LOOP ; 继续外层循环
; 程序结束
MOV AH, 4CH ; DOS程序结束功能
INT 21H
MAIN ENDP
CODE ENDS
END MAIN
运行前:

运行后:

17. 已知数 A = 9876,数 B = 6543,编程求两数之和。
解:
; 定义数据段
DATA SEGMENT
A DW 9876 ; 定义数A,16进制为2694H
B DW 6543 ; 定义数B,16进制为198FH
SUM DW ? ; 存储和,预期结果为2694+198F=4023H
DATA ENDS
; 定义堆栈段
STACK SEGMENT PARA STACK 'STACK'
STAPN DB 100 DUP(?) ; 分配100字节堆栈空间
TOP EQU LENGTH STAPN ; 定义堆栈顶部位置
STACK ENDS
; 定义代码段
CODE SEGMENT
ASSUME CS:CODE, DS:DATA, SS:STACK
; 主程序
MAIN PROC FAR
; 设置堆栈段
MOV AX, STACK
MOV SS, AX
MOV SP, TOP ; 设置堆栈指针
; DOS程序返回设置
PUSH DS
XOR AX, AX
PUSH AX
; 设置数据段
MOV AX, DATA
MOV DS, AX
; 计算两数之和
MOV AX, A ; AX = A (9876)
ADD AX, B ; AX = A + B (9876 + 6543 = 16419)
MOV SUM, AX ; 保存结果
; 程序结束
MOV AH, 4CH ; DOS程序结束功能
INT 21H
MAIN ENDP
CODE ENDS
END MAIN
运行前:

运行后:

18. 某班有 20 个同学的微机原理成绩存放在 LIST 开始的单元中,要求编程先按从高到低的次序排列好,再求出总分,并将其存放到 SUM 开始的单元中。
解1:
对LIST使用16进制显示十进制,即"65H"代表十进制"65",使用BCD码求和
STACK SEGMENT PARA STACK 'STACK'
DB 100 DUP (?) ; 定义堆栈段
STACK ENDS
DATA SEGMENT
LIST DB 65H,76H,78H,54H,90H,85H,68H,66H,77H,88H
DB 99H, 89H, 79H, 69H,75H,85H,63H,73H,83H,93H
SUM DW 0 ; 存储总和,预期结果十进制为1555,BCD码为0F55,
DATA ENDS
CODE SEGMENT
ASSUME CS:CODE, DS:DATA, SS:STACK
START:
; 设置堆栈和数据段
PUSH DS ; 保存原始DS
MOV AX, DATA
MOV DS, AX ; 设置DS指向数据段
; ==== 冒泡排序(降序排序) ====
MOV DI, OFFSET LIST ; DI指向数组首地址
MOV BX, 19 ; 设置外层循环次数(n-1=19)
LP0:
MOV SI, DI ; SI指向数组首元素
MOV CX, BX ; 设置内层循环次数
LP1:
MOV AL, [SI] ; 取当前元素
INC SI ; 指向下一个元素
CMP AL, [SI] ; 比较当前元素和下一个元素
JNC LP2 ; 如果AL >= [SI](无进位),跳过交换
; 交换相邻的两个元素
MOV DL, [SI] ; 保存后一个元素
MOV [SI-1], DL ; 将后一个元素存入前一个位置
MOV [SI], AL ; 将前一个元素存入后一个位置
LP2:
LOOP LP1 ; 内层循环
DEC BX ; 外层循环次数减1
JNZ LP0 ; 如果BX不为0,继续外层循环
; ==== 计算总和和平均值 ====
MOV CX, 20 ; 设置循环次数(20个元素)
MOV BX, OFFSET LIST ; BX指向数组开头
MOV SUM, 0 ; 清零SUM
XOR AX, AX ; 清空AX(用于累加)
LP4:
ADD AL, [BX] ; 加一个元素到AL
DAA ; 十进制调整(BCD运算)
ADC AH, 0 ; 处理进位(进位加到AH)
INC BX ; 指向下一个元素
LOOP LP4 ; 继续循环
MOV SUM, AX ; 保存总和
; 恢复原始DS(为返回DOS准备)
POP DS
; 程序结束(返回到DOS)
MOV AH, 4CH
INT 21H
CODE ENDS
END START

解2:
STACK SEGMENT PARA STACK 'STACK'
DB 100 DUP (?) ; 定义堆栈段
STACK ENDS
DATA SEGMENT
; 使用十进制表示数据(去掉H后缀)
LIST DB 65,76,78,54,90,85,68,66,77,88
DB 99,89,79,69,75,85,63,73,83,93
SUM DW 0 ; 存储总和,预期结果十进制为1555,十六进制为0613H
DATA ENDS
CODE SEGMENT
ASSUME CS:CODE, DS:DATA, SS:STACK
START:
; 设置堆栈和数据段
PUSH DS ; 保存原始DS
MOV AX, DATA
MOV DS, AX ; 设置DS指向数据段
; ==== 冒泡排序(降序排序) ====
MOV DI, OFFSET LIST ; DI指向数组首地址
MOV BX, 19 ; 设置外层循环次数(n-1=19)
LP0:
MOV SI, DI ; SI指向数组首元素
MOV CX, BX ; 设置内层循环次数
LP1:
MOV AL, [SI] ; 取当前元素
INC SI ; 指向下一个元素
CMP AL, [SI] ; 比较当前元素和下一个元素
JNC LP2 ; 如果AL >= [SI](无进位),跳过交换
; 交换相邻的两个元素
MOV DL, [SI] ; 保存后一个元素
MOV [SI-1], DL ; 将后一个元素存入前一个位置
MOV [SI], AL ; 将前一个元素存入后一个位置
LP2:
LOOP LP1 ; 内层循环
DEC BX ; 外层循环次数减1
JNZ LP0 ; 如果BX不为0,继续外层循环
; ==== 计算总和(普通二进制加法) ====
MOV CX, 20 ; 设置循环次数(20个元素)
MOV BX, OFFSET LIST ; BX指向数组开头
XOR AX, AX ; 清空AX(用于累加)
LP4:
XOR DH, DH ; 清空DH(用于零扩展)
MOV DL, [BX] ; 加载字节到DL
ADD AX, DX ; 将DX加到AX中(16位加法)
INC BX ; 指向下一个元素
LOOP LP4 ; 继续循环
MOV SUM, AX ; 保存总和
; 恢复原始DS(为返回DOS准备)
POP DS
; 程序结束(返回到DOS)
MOV AH, 4CH
INT 21H
CODE ENDS
END START
19. 编程将后跟$符的字符串“Go to School.”中的小写字母都改成大写字母。(提示:小写字母比大写字母的 ASCII 码大 20H,如‘A’= 41H,‘a’= 61H。)
解:
; 定义堆栈段
STACK SEGMENT PARA STACK 'STACK'
DB 100 DUP(?) ; 分配100字节的堆栈空间
STACK ENDS
; 定义数据段
DATA SEGMENT
STRING DB 'Go to School.$' ; 以$结尾的字符串
DATA ENDS
; 定义代码段
CODE SEGMENT
ASSUME CS:CODE, DS:DATA, SS:STACK
; 主程序
MAIN PROC FAR
; 初始化堆栈和数据段
MOV AX, DATA
MOV DS, AX
; 准备遍历字符串
LEA SI, STRING ; SI指向字符串开头
CONVERT_LOOP:
MOV AL, [SI] ; 取一个字符
CMP AL, '$' ; 检查是否结束
JE DISPLAY ; 遇到$结束符,跳转到显示
; 检查是否是小写字母 (a-z)
CMP AL, 'a' ; 小于'a'?
JB NOT_LOWER ; 不是小写字母
CMP AL, 'z' ; 大于'z'?
JA NOT_LOWER ; 不是小写字母
; 是小写字母,转换为大写
SUB AL, 20H ; 小写转大写 (减32)
MOV [SI], AL ; 保存回原位置
NOT_LOWER:
INC SI ; 移动到下一个字符
JMP CONVERT_LOOP ; 继续处理
DISPLAY:
; 显示转换后的字符串
LEA DX, STRING ; DX指向字符串
MOV AH, 09H ; DOS显示字符串功能
INT 21H
; 程序结束,返回DOS
MOV AH, 4CH
INT 21H
MAIN ENDP
CODE ENDS
END MAIN
20. 编程将存放在 AL 中的无符号二进制数,转换成十六进制数,再转换成 ASCII 码并显示在屏幕上。
解:
;测试数据比如AL=10H,屏幕就显示10,如果AL=1AH,屏幕就显示1A
; ------------------- 堆栈段定义 -------------------
STACK SEGMENT STACK
DB 100 DUP(?) ; 预留100字节堆栈空间(满足8086堆栈需求)
STACK ENDS
; ------------------- 数据段定义(示例结构,无有效数据) -------------------
DATA SEGMENT
DB 100 DUP(?) ; 预留100字节数据空间(实际未存储有效数据,仅演示段结构)
DATA ENDS
; ------------------- 代码段定义 -------------------
CODE SEGMENT
ASSUME CS:CODE, DS:DATA, SS:STACK ; 关联段寄存器(CS→代码段, DS→数据段, SS→堆栈段)
START: ; 程序入口点
PUSH DS ; 保存原始DS寄存器(兼容DOS环境的标准操作)
MOV AX, DATA ; 初始化数据段:将DATA段基地址送入AX
MOV DS, AX ; 将AX值送入DS,完成DS初始化
MOV AL,00011010B ;⚠️假设测试数据比如AL=10H,屏幕就显示10,如果AL=1AH,屏幕就显示1A
MOV DH, AL ; 假设AL已预先存储待显示的字节数据(需补充)
MOV CH, 02H ; 设置循环次数(显示2个十六进制位:高4位 + 低4位)
ST1: ; 循环处理高/低4位
MOV CL, 4 ; 每次右移4位(处理单个十六进制位)
ROR DH, CL ; 对DH循环右移4位 → 将当前待显示的4位移到低4位,比如DX=1000H->0100H
MOV AL, DH ; 暂存处理后的数据到AL
AND AL, 0FH ; 屏蔽高4位 → 仅保留低4位(待显示的十六进制位)
ADD AL, 30H ; 转换为ASCII码(先按数字'0'-'9'处理)
CMP AL, 39H ; 判断是否为字母(A-F)或数字(0-9)
JBE DISP ; 若为数字(≤'9'),直接跳转显示P
ADD AL, 07H ; 若为字母(A-F),额外加7(ASCII中A=41H,9=39H,差值为7)
DISP: ; 显示单个字符
MOV DL, AL ; 待显示字符送入DL(DOS显示功能的参数寄存器)
MOV AH, 02H ; DOS功能调用:AH=02H → 显示DL中的字符
INT 21H ; 执行DOS中断,显示字符
DEC CH ; 循环次数减1
JNZ ST1 ; 若循环次数未到(CH≠0),继续处理下一个4位
; 程序退出
POP DS ; 恢复原始DS寄存器
MOV AH, 4CH ; DOS功能调用:AH=4CH → 程序终止并返回操作系统
INT 21H ; 执行中断
HLT ; 暂停CPU(可选,INT 21H已完成退出)
CODE ENDS ; 代码段结束
END START ; 程序入口点标记
输出结果:
假设AL=00011010B,即1AH

21.
将 BX 中的十六进制数(<9999)转换成 4 位压缩 BCD 数,存入 CX 中。(参考例 4.44。)
解法1:
统一使用字除法,DX<-余数,AX<-商
; ------------------- 堆栈段定义 -------------------
STACK SEGMENT STACK
DB 100 DUP(?) ; 预留100字节堆栈空间
STACK ENDS
; ------------------- 数据段定义(可按需扩展) -------------------
DATA SEGMENT
; 若需存储测试数据或中间结果,可在此定义
DATA ENDS
; ------------------- 代码段定义 -------------------
CODE SEGMENT
ASSUME CS:CODE, DS:DATA, SS:STACK ; 关联段寄存器
; ==================== 主程序(测试入口) ====================
MAIN PROC FAR
MOV AX, DATA ; 初始化数据段寄存器DS
MOV DS, AX ; DS指向DATA段
; 测试:将BX设为十进制 9346(十六进制 0x2482)
MOV BX, 9346 ; BX = 待转换的十六进制数(需 < 9999)
;9346的十六进制为2482H
CALL BIN_BCD21 ; 调用转换过程,结果存入CX
; 程序退出(返回DOS)
MOV AH, 4CH
INT 21H
MAIN ENDP
; ==================== 转换过程:BX → CX(4位压缩BCD) ====================
; --------------------------------------------------------
; 题 21:将 BX 中的十六进制数(< 9999)转换成 4 位压缩 BCD 数,
; 存入 CX 中。
; 输入:BX = 待转换的二进制数 (< 9999)
; 输出:CX = 压缩后的 BCD(4 位,每位 4 bit)
; 使用寄存器:AX, DX, SI
; --------------------------------------------------------
BIN_BCD21 PROC NEAR
; 比较 BX 与 9999,若大于则直接返回(CX 未定义)
MOV AX, BX
CMP AX, 9999
JA EXIT21
; 第一轮:除以 1000,得到千位
XOR DX, DX ; 清除 DX,高 16 位被除数 = 0
MOV AX, BX ; AX ← 原数
MOV SI, 1000
DIV SI ; (DX:AX)/1000 → AX=千位商, DX=余数,AX=346,DX=9
MOV CX, AX ; CX ← 千位
; 第二轮:对余数除以 100,得到百位
MOV AX, DX ; AX ← 上轮余数
XOR DX, DX ; 清除 DX
MOV SI, 100 ;由于SI为字节,所以AX=百位商, DX=新余数,此处并没有继续沿用例4.44的换成字节的除法方法
DIV SI ; AX=百位商, DX=新余数,AH=,DX=46,AX=3
; CX <<= 4 (用 4 次 SHL CX,1)
SHL CX, 1 ; CX 左移 4 位,为下一位腾出空间
SHL CX, 1 ; CX 左移 4 位,为下一位腾出空间
SHL CX, 1 ; CX 左移 4 位,为下一位腾出空间
SHL CX, 1 ; CX 左移 4 位,为下一位腾出空间
OR CX, AX ; 低 4 位或入百位商
; 第三轮:对余数除以 10,得到十位
MOV AX, DX ; AX ← 上轮余数
XOR DX, DX ; 清除 DX
MOV SI, 10
DIV SI ; AX=十位商, DX=余数(个位)
; CX <<= 4 (用 4 次 SHL CX,1)
SHL CX, 1 ; CX 左移 4 位,为下一位腾出空间
SHL CX, 1 ; CX 左移 4 位,为下一位腾出空间
SHL CX, 1 ; CX 左移 4 位,为下一位腾出空间
SHL CX, 1 ; CX 左移 4 位,为下一位腾出空间
OR CX, AX ; 或入十位商
; 第四轮:直接把余数(个位)放入低 4 位
; CX <<= 4 (用 4 次 SHL CX,1)
SHL CX, 1 ; CX 左移 4 位,为下一位腾出空间
SHL CX, 1 ; CX 左移 4 位,为下一位腾出空间
SHL CX, 1 ; CX 左移 4 位,为下一位腾出空间
SHL CX, 1 ; CX 左移 4 位,为下一位腾出空间
OR CX, DX ; 或入个位余数
EXIT21:
RET
BIN_BCD21 ENDP
最后CX结果:

解法2:原封不动使用原来的例4.44代码,把最后结果的DX复制到CX中
;----------------------------------------------------------
; 完整程序框架,使用 STACK/DATA/CODE 段,
; 题 21:将 BX 中的十六进制数(< 9999)转换成 4 位压缩 BCD,存入 CX
; 环境:MASM/TASM 16 位
;----------------------------------------------------------
STACK SEGMENT STACK
DB 100h DUP(?) ; 256 字节栈空间
STACK ENDS
DATA SEGMENT
; (本例无需额外数据,留空)
DATA ENDS
CODE SEGMENT
ASSUME CS:CODE, DS:DATA, SS:STACK
STARTUP:
;—— 初始化段寄存器 ——
MOV AX, DATA
MOV DS, AX
MOV AX, STACK
MOV SS, AX
MOV SP, 100h ; SP 指向栈顶
;—— 测试:将 BX 置为 9346 ——
MOV BX, 9346
;—— 调用 BCD 转换子程序 ——
CALL BIN_BCD21
;—— 返回 DOS ——
MOV AH, 4Ch
INT 21h
;----------------------------------------------------------
; 子程序:BIN_BCD21
; 输入:BX = 待转换的二进制数 (<9999)
; 输出:CX = 4×4bit 压缩 BCD
; 破坏:AX, DX, CX, CL
;----------------------------------------------------------
BIN_BCD21 PROC NEAR
;—— 判断 BX 是否超限 ——
CMP BX, 9999
JBE TRAN21 ; 若 BX ≤ 9999 则转去转换
JMP EXIT21 ; 否则直接返回(CX 未定义)
TRAN21:
;—— 第 1 步:除以 1000,取商(千位)和余数 ——
SUB DX, DX ; 清 DX
MOV AX, BX ; AX ← 待除数
MOV CX, 1000 ; CX ← 除数 1000
DIV CX ; (DX:AX)/1000 → AX=商(千位), DX=余数
XCHG AX, DX ; 交换,DX=商(千位),AX=余数
;—— 将千位 商 左移 4 位 ——
MOV CL, 4
SHL DX, CL ; DX = 千位 × 16
;—— 第 2 步:对余数再除以 100,取商(百位)和余数 ——
MOV CL, 100
DIV CL ; AX/100 → AL=商(百位), AH=余数
ADD DL, AL ; 累加到 DX 低 8 位:DX = 千位×16 + 百位
;—— DX 左移 4 位,为下次百→十腾空间 ——
MOV CL, 4
SHL DX, CL
;—— 第 3 步:对剩余 AH(百步余数)除以 10,取商(十位)和余数 ——
XCHG AL, AH ; 把余数移入 AL
MOV AH, 0 ; 清 AH,准备做 AX/10
MOV CL, 10
DIV CL ; AX/10 → AL=商(十位), AH=余数(个位)
ADD DL, AL ; 累加到 DX:DX = 高两位 + 十位
;—— DX 左移 4 位,为个位腾空间 ——
MOV CL, 4
SHL DX, CL
;—— 第 4 步:把 AH(个位余数)合入 ——
ADD DL, AH ; DX = 千百十位<<4 + 个位
;—— 最终结果搬到 CX ——
MOV CX, DX
EXIT21:
RET
BIN_BCD21 ENDP
CODE ENDS
END STARTUP
输出结果

补充开头另一种快速写法
;----------------------------------------------------------
; 题 21:将 BX 中的十六进制数(< 9999)转换成 4 位压缩 BCD,存入 CX
; 环境:MASM/TASM 语法
;----------------------------------------------------------
.model small ; 必需 定义内存模型(决定代码 / 数据段布局) 指定内存模型为小模型(代码段和数据段分开)
.stack 100h ;推荐 定义堆栈大小(未声明时使用默认堆栈) 设置堆栈段大小为 256 字节(100H)
.data
.code ;必需 标记代码段开始(没有它汇编器不知道代码位置) 代码段开始
BIN_BCD21 PROC NEAR
;—— 判断 BX 是否超限 ——
MOV BX,9346
CMP BX, 9999
JBE TRAN21 ; 若 BX ≤ 9999 则转去转换
JMP EXIT21 ; 否则直接返回(CX 未定义)
TRAN21:
;—— 第 1 步:除以 1000,取商(千位)和余数 ——
SUB DX, DX ; 清 DX
MOV AX, BX ; AX ← 待除数
MOV CX, 1000 ; CX ← 除数 1000
DIV CX ; (DX:AX)/1000 → AX=商(千位), DX=余数
XCHG AX, DX ; 交换,DX=商(千位),AX=余数
;—— 将千位 商 左移 4 位 ——
MOV CL, 4
SHL DX, CL ; DX = 千位 × 16
;—— 第 2 步:对余数再除以 100,取商(百位)和余数 ——
MOV CL, 100
DIV CL ; AX/100 → AL=商(百位), AH=余数
ADD DL, AL ; 累加到 DX 低 8 位:DX = 千位×16 + 百位
;—— DX 左移 4 位,为下次百→十腾空间 ——
MOV CL, 4
SHL DX, CL
;—— 第 3 步:对剩余 AH(百步余数)除以 10,取商(十位)和余数 ——
XCHG AL, AH ; 把余数移入 AL
MOV AH, 0 ; 清 AH,准备做 AX/10
MOV CL, 10
DIV CL ; AX/10 → AL=商(十位), AH=余数(个位)
ADD DL, AL ; 累加到 DX:DX = 高两位 + 十位
;—— DX 左移 4 位,为个位腾空间 ——
MOV CL, 4
SHL DX, CL
;—— 第 4 步:把 AH(个位余数)合入 ——
ADD DL, AH ; DX = 千百十位<<4 + 个位
;—— 最终结果搬到 CX ——
MOV CX, DX
EXIT21:
RET
BIN_BCD21 ENDP
END BIN_BCD21
时隔一年补完了第四章的答案,过去一年误导了多少人使用旧版第四版的错误答案🙇♀️
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)