被骂了,我一个都快毕业的大四回来做大三的课

1.

从编写汇编语言源程序到生成可执行文件.EXE,需要经过哪些步骤?

解:

见书本P117页

首先,用编辑程序EDIT将用户编写的汇编语言程序键入计算机,按照规定格式编排后,以文件的形式存放在磁盘上。文件由文件名和扩展名两部分组成,扩展名前面要加一个圆点“.”,汇编过程中使用的文件名可以由用户自行定义,这里假设文件名为PROG。扩展名是由系统规定的,汇编语言源程序、目标文件和可执行文件的扩展名分别为.ASM、.OBJ和.EXE。

汇编语言程序经编辑程序EDIT编排后生成汇编语言源程序PROG.ASM,经汇编程序MASM汇编后生成目标文件PROG.OBJ,此时还可以生成一个可选的列表文件PROG.LIST,目标文件经连接程序LINK连接后,生成可执行文件PROG.EXE。


2. 指令语句和伪指令语句各由哪几个字段组成?哪些字段是必不可少的?

指令语句的组成字段:

指令语句由以下4个字段组成:

  1. 标号:表示指令的符号地址,后跟冒号“:”。(可选)
  2. 指令助记符:表示指令的操作码。(必不可少)
  3. 操作数:指令的操作对象,可以是常数、变量、寄存器等。(根据指令需求,可能没有、一个或多个)
  4. 注释:用来说明指令功能,以分号“;”开头。(可选)

必不可少的字段:指令助记符。

伪指令语句的组成字段:

伪指令语句由以下4个字段组成:

  1. 名字:伪指令语句的名称,符号地址表示。(可选,但某些伪指令必须)
  2. 伪指令指示符:MASM规定的伪指令符号(如SEGMENT、DB等)。(必不可少)
  3. 操作数:根据伪指令要求确定,可能没有、一个或多个。(根据伪指令需求)
  4. 注释:说明伪指令功能,以分号“;”开头。(可选)

必不可少的字段:伪指令指示符。


3. 伪指令语句的作用是什么?它与指令语句的主要区别是什么?

伪指令语句是指示性语句,没有对应的机器码,不直接由CPU执行,而是在汇编过程中由汇编程序处理,用于完成以下功能:

  • 定义程序结构(如段定义SEGMENT/ENDS、过程定义PROC/ENDP)。
  • 分配存储空间或初始化数据(如DB、DW、DD等)。
  • 指定段与段寄存器的关系(如ASSUME)。
  • 控制汇编过程(如程序结束END)。

与指令语句的主要区别:

  1. 功能不同
    1. 指令语句:对应CPU可执行的机器指令,完成实际运算或操作(如MOV、ADD等)
    2. 伪指令语句:指导汇编程序如何汇编,不生成机器码。
  2. 执行时机
    • 指令语句:在程序运行时由CPU执行。
    • 伪指令语句:在汇编阶段由汇编程序处理。
  3. 字段格式
    • 指令语句的标号后加冒号“:”,伪指令语句的名字后不加冒号。
    • 指令语句的核心是指令助记符,伪指令语句的核心是伪指令指示符。

关键区别:伪指令是汇编时的控制命令,指令是运行时的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)

详细说明:

  1. MOV AL, TYPE X1

    • TYPE 运算符返回变量的字节大小
    • X1 是 DB(字节)类型 → 大小 = 1 字节
    • 结果:AL = 01H
  2. MOV BL, TYPE X2

    • X2 是 DW(字)类型 → 大小 = 2 字节
    • 结果:BL = 02H
  3. MOV CL, TYPE X3

    • X3 是 DD(双字)类型 → 大小 = 4 字节
    • 结果:CL = 04H
  4. MOV AH, TYPE GO

    • TYPE 对标号返回类型值:NEAR = -1(0FFFFH),FAR = -2(0FFFEH)
    • GO 是默认的 NEAR 标号 → 类型值 = 0FFFFH
    • 截取低8位 → 0FFH(因为AH是8位寄存器)
    • 结果:AH = 0FFH
  5. MOV BH, SIZE X2

    • SIZE = LENGTH × TYPE
    • X2 无 DUP 定义 → LENGTH = 1
    • TYPE X2 = 2 → SIZE = 1 × 2 = 2
    • 结果:BH = 02H
  6. MOV CH, LENGTH X3

    • LENGTH 返回元素数量(无 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完整的汇编语言程序框架

伪指令功能说明

  1. SEGMENT/ENDS

    • 功能:定义逻辑段(数据段/附加段/堆栈段/代码段)
    • 说明:成对使用,SEGMENT标记段开始,ENDS标记段结束
    • 示例:DATA SEGMENT定义数据段开始,DATA ENDS定义结束
  2. DB/DW

    • 功能:定义变量并分配存储空间
    • DB:定义字节类型(8位)
    • DW:定义字类型(16位)
    • 示例:X DB ?分配1字节未初始化空间
  3. DUP(?)

    • 功能:重复分配未初始化空间
    • 示例:DB 100 DUP(?)分配100字节未初始化空间
  4. EQU

    • 功能:定义符号常量(不分配空间)
    • 示例:TOP EQU LENGTH STAPN定义TOP为STAPN的长度值
  5. PROC/ENDP

    • 功能:定义过程(子程序)
    • FAR:指定为远过程(可跨段调用)
    • 示例:MAIN PROC FAR定义主过程开始,MAIN ENDP结束定义
  6. ASSUME

    • 功能:指定段寄存器与逻辑段的关联关系
    • 说明:仅告知汇编程序,不实际加载段寄存器
    • 示例:ASSUME CS:CODE指定CS指向代码段
  7. 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)

  1. 设置功能号:将功能号(00H~6CH)送入AH寄存器

  2. 设置入口参数:根据功能要求设置相关寄存器

  3. 执行中断指令:调用INT 21H

  4. 处理出口参数:从指定寄存器获取返回结果

示例:
MOV AH, 02H ; 功能号02H(显示字符)
MOV DL, 'A' ; 入口参数
INT 21H ; 执行调用
; 出口:屏幕显示'A'

BIOS中断调用步骤(使用INT n)

  1. 设置功能号:将子功能号送入AH寄存器

  2. 设置入口参数:初始化相关寄存器

  3. 执行指定中断:调用对应的INT n指令

  4. 分析出口状态:检查返回寄存器和状态标志

示例(读取键盘状态):
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

时隔一年补完了第四章的答案,过去一年误导了多少人使用旧版第四版的错误答案🙇‍♀️

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐