以下是对《汇编程序语言》中这几章内容的详细介绍:
第六章 循环结构程序
一、循环结构概述
- 概念:循环结构允许程序在满足特定条件时,重复执行一段代码。这在需要多次执行相同或相似操作的场景中非常有用,例如对数组的每个元素进行处理,或者进行多次迭代计算。通过循环结构,可以避免重复编写相同的代码,提高程序的效率和可读性。
- 循环组成要素:
- 初始化部分:在循环开始前,对循环控制变量和相关数据进行初始化。例如,设置循环计数器的初始值,初始化累加器等。
- 循环体:这是需要重复执行的代码段,包含了实际的操作逻辑,如数据处理、计算等。
- 循环控制部分:负责检查循环条件是否满足,决定是否继续执行循环体。它会根据循环控制变量的值或其他条件进行判断,并且在每次循环结束后更新循环控制变量。
二、循环控制指令
- 计数控制循环:
LOOP
指令:该指令基于CX
寄存器作为计数器。执行LOOP
指令时,CX
寄存器的值会自动减 1,然后检查CX
是否为 0。如果CX
不为 0,则跳转到指定的目标地址继续执行循环体;如果CX
为 0,则退出循环,继续执行LOOP
指令后面的代码。例如:
MOV CX, 10 ; 初始化循环计数器为 10
START_LOOP:
; 循环体代码
LOOP START_LOOP ; CX 减 1,若不为 0 则跳转回 START_LOOP
- 条件控制循环:
JCXZ
指令与条件结合:JCXZ
(Jump if CX is Zero)指令用于在CX
寄存器为 0 时跳转。可以结合其他条件判断指令,实现条件控制的循环。例如,在一段代码中,先通过比较指令设置标志位,然后根据标志位和CX
的值决定是否继续循环。
MOV CX, 10
CHECK_CONDITION:
CMP AX, BX ; 比较 AX 和 BX
JGE END_LOOP ; 如果 AX >= BX,结束循环
; 循环体代码
LOOP CHECK_CONDITION
END_LOOP:
; 循环结束后的代码
三、循环结构程序设计实例
- 累加求和:计算从 1 到 100 的整数之和。
- 思路:使用一个寄存器作为累加器(如
AX
),另一个寄存器作为循环计数器(如CX
)。在循环体中,将计数器的值累加到累加器中,每次循环计数器加 1,直到计数器达到 100 时结束循环。 - 示例代码:
- 思路:使用一个寄存器作为累加器(如
DATA SEGMENT
SUM DW?
DATA ENDS
CODE SEGMENT
ASSUME CS:CODE, DS:DATA
START:
MOV AX, DATA
MOV DS, AX
MOV AX, 0 ; 初始化累加器
MOV CX, 100 ; 初始化循环计数器
ADD_LOOP:
ADD AX, CX ; 累加
LOOP ADD_LOOP
MOV SUM, AX ; 保存结果
MOV AH, 4CH
INT 21H ; 返回 DOS
CODE ENDS
END START
- 数组元素处理:假设有一个包含 10 个整数的数组,将每个元素乘以 2。
- 思路:通过循环遍历数组,每次从数组中取出一个元素,乘以 2 后再存回数组。使用一个寄存器作为数组索引,每次循环递增索引以访问下一个数组元素。
- 示例代码:
DATA SEGMENT
ARRAY DW 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
COUNT EQU ($ - ARRAY) / 2 ; 计算数组元素个数
DATA ENDS
CODE SEGMENT
ASSUME CS:CODE, DS:DATA
START:
MOV AX, DATA
MOV DS, AX
MOV CX, COUNT
MOV SI, 0 ; 数组索引初始化
MULTIPLY_LOOP:
MOV AX, ARRAY[SI]
SHL AX, 1 ; AX 乘以 2
MOV ARRAY[SI], AX
ADD SI, 2 ; 移动到下一个数组元素
LOOP MULTIPLY_LOOP
MOV AH, 4CH
INT 21H ; 返回 DOS
CODE ENDS
END START
第七章 子程序
一、子程序概念
- 定义:子程序是一段具有特定功能的独立代码块,可以在程序的不同地方被调用。它类似于高级语言中的函数,通过将常用的功能封装成子程序,可以提高代码的复用性,减少重复代码,使程序结构更加清晰。
- 作用:提高程序的模块化程度,便于代码的维护和扩展。例如,在一个大型程序中,如果有多个地方需要进行字符串比较操作,将字符串比较功能编写成一个子程序,在需要时调用该子程序,而不需要在每个地方都重新编写相同的代码。
二、子程序的调用与返回
- 调用指令:
CALL
指令:用于调用子程序。当执行CALL
指令时,CPU 会将CALL
指令的下一条指令的地址(返回地址)压入堆栈,然后跳转到子程序的入口地址执行子程序代码。CALL
指令有不同的寻址方式,如直接调用(CALL SUBROUTINE_LABEL
)和间接调用(CALL [BX]
等)。
- 返回指令:
RET
指令:用于子程序结束时返回调用点。RET
指令从堆栈中弹出返回地址,将其装入指令指针寄存器(IP
),使程序返回到调用子程序的下一条指令继续执行。对于带参数的子程序调用,还可能有RET n
形式的指令,其中n
是一个立即数,用于在返回时调整堆栈指针,跳过在堆栈中传递的参数。
三、子程序设计与参数传递
- 子程序设计原则:
- 功能单一性:每个子程序应只完成一个明确的功能,这样便于理解、调试和维护。例如,一个子程序专门用于计算两个数的和,另一个子程序用于字符串的复制。
- 通用性:尽量使子程序具有通用性,能够适应不同的输入参数,提高代码的复用性。
- 参数传递方法:
- 寄存器传递参数:将参数存放在特定的寄存器中,在调用子程序前,调用者将参数装入相应寄存器,子程序从寄存器中获取参数进行处理。例如,将两个要相加的数分别存放在
AX
和BX
寄存器中,然后调用加法子程序,子程序从AX
和BX
中取出数进行相加。 - 堆栈传递参数:调用者将参数依次压入堆栈,子程序从堆栈中取出参数。在子程序返回前,需要注意恢复堆栈指针,以保证程序的正常运行。例如:
- 寄存器传递参数:将参数存放在特定的寄存器中,在调用子程序前,调用者将参数装入相应寄存器,子程序从寄存器中获取参数进行处理。例如,将两个要相加的数分别存放在
; 调用者代码
PUSH PARAM1
PUSH PARAM2
CALL ADD_SUBROUTINE
ADD SP, 4 ; 恢复堆栈指针
; 子程序代码
ADD_SUBROUTINE PROC
POP BX ; 取出第二个参数
POP AX ; 取出第一个参数
ADD AX, BX
RET
ADD_SUBROUTINE ENDP
- **内存变量传递参数**:通过在内存中定义变量来传递参数。调用者将参数存入内存变量,子程序通过访问这些内存变量获取参数。这种方法适用于参数较多或参数为复杂数据结构的情况。
四、子程序设计实例
- 计算两个数的最大公约数(欧几里得算法):
- 思路:使用欧几里得算法,通过反复用较大数除以较小数,并将余数作为新的较小数,原来的较小数作为新的较大数,直到余数为 0,此时的较小数就是最大公约数。将这个计算过程编写成一个子程序。
- 示例代码:
DATA SEGMENT
NUM1 DW 12
NUM2 DW 18
GCD DW?
DATA ENDS
CODE SEGMENT
ASSUME CS:CODE, DS:DATA
START:
MOV AX, DATA
MOV DS, AX
MOV AX, NUM1
MOV BX, NUM2
CALL EUCLID_GCD
MOV GCD, AX
MOV AH, 4CH
INT 21H ; 返回 DOS
EUCLID_GCD PROC
PUSH BP
MOV BP, SP
MOV AX, [BP + 4] ; 取出第一个参数
MOV BX, [BP + 6] ; 取出第二个参数
GCD_LOOP:
CMP BX, 0
JE GCD_EXIT
MOV DX, 0
DIV BX
MOV AX, BX
MOV BX, DX
JMP GCD_LOOP
GCD_EXIT:
POP BP
RET 4 ; 返回并调整堆栈指针
EUCLID_GCD ENDP
CODE ENDS
END START
第八章 数值运算
一、算术运算指令
- 加法运算:
ADD
指令:用于将两个操作数相加,结果存放在目的操作数中。例如,ADD AX, BX
将AX
和BX
中的值相加,结果存放在AX
中。该指令会影响标志寄存器中的CF
(进位标志)、OF
(溢出标志)、ZF
(零标志)等标志位,以反映运算结果的特征。ADC
指令:带进位加法指令,用于多字节加法运算。在进行多字节加法时,低字节相加可能产生进位,ADC
指令在相加时会把低字节相加产生的进位一起加上。例如,在计算两个 32 位整数相加时,先使用ADD
指令计算低 16 位,然后使用ADC
指令计算高 16 位,并加上低 16 位相加产生的进位。
- 减法运算:
SUB
指令:从目的操作数中减去源操作数,结果存放在目的操作数中。例如,SUB CX, DX
从CX
中减去DX
的值,结果存于CX
。同样会影响标志寄存器的相关标志位,如CF
(借位标志,当减法运算需要借位时CF = 1
)、OF
、ZF
等。SBB
指令:带借位减法指令,用于多字节减法运算,在减法时会考虑低位字节减法产生的借位。
- 乘法运算:
- 无符号乘法
MUL
指令:用于无符号数乘法。如果是 8 位乘法,如MUL BL
,则将AL
中的值与BL
中的值相乘,结果的低 8 位存放在AL
中,高 8 位存放在AH
中;如果是 16 位乘法,如MUL BX
,则将AX
中的值与BX
中的值相乘,结果的低 16 位存放在AX
中,高 16 位存放在DX
中。 - 有符号乘法
IMUL
指令:用于有符号数乘法,运算规则与MUL
类似,但考虑了操作数的符号,结果也按照有符号数的规则存放。
- 无符号乘法
- 除法运算:
- 无符号除法
DIV
指令:用于无符号数除法。例如,8 位除法时,被除数存放在AX
中,除数为 8 位寄存器或内存字节单元,执行DIV BL
后,商存放在AL
中,余数存放在AH
中;16 位除法时,被除数为 32 位,存放在DX:AX
中,除数为 16 位寄存器或内存字单元,结果商存放在AX
中,余数存放在DX
中。 - 有符号除法
IDIV
指令:用于有符号数除法,操作数和结果都按照有符号数处理。
- 无符号除法
二、数值运算程序设计实例
- 多字节加法:实现两个 32 位无符号整数的加法。
- 思路:将 32 位整数分为高 16 位和低 16 位,先使用
ADD
指令计算低 16 位的和,再使用ADC
指令计算高 16 位的和,并加上低 16 位相加产生的进位。 - 示例代码:
- 思路:将 32 位整数分为高 16 位和低 16 位,先使用
DATA SEGMENT
NUM1 DW 1234H, 5678H
NUM2 DW 9ABC H, DEF0H
SUM DW 2 DUP(?)
DATA ENDS
CODE SEGMENT
ASSUME CS:CODE, DS:DATA
START:
MOV AX, DATA
MOV DS, AX
MOV AX, NUM1
ADD AX, NUM2
MOV SUM, AX
MOV AX, NUM1 + 2
ADC AX, NUM2 + 2
MOV SUM + 2, AX
MOV AH, 4CH
INT 21H ; 返回 DOS
CODE ENDS
END START
- 高精度乘法:实现两个 16 位无符号整数相乘,结果为 32 位。
- 思路:使用移位相加法进行乘法运算。将一个乘数逐位左移,当某位为 1 时,将另一个乘数加到结果中。
- 示例代码:
DATA SEGMENT
MULT1 DW 1234H
MULT2 DW 5678H
PRODUCT DD?
DATA ENDS
CODE SEGMENT
ASSUME CS:CODE, DS:DATA
START:
MOV AX, DATA
MOV DS, AX
MOV AX, 0
MOV DX, 0
MOV CX, 16
MULT_LOOP:
SHL MULT2, 1
JNC NO_ADD
ADD AX, MULT1
ADC DX, 0
NO_ADD:
SHL MULT1, 1
LOOP MULT_LOOP
MOV PRODUCT, AX
MOV PRODUCT + 2, DX
MOV AH, 4CH
INT 21H ; 返回 DOS
CODE ENDS
END START
以下为《汇编程序语言》第九章“代码转换”和第十章“列表处理”的详细内容,并对每行代码添加了注释:
第九章 代码转换
二进制与十进制转换
二进制转十进制
DATA SEGMENT
BINARY_NUM DW 1234H ; 定义一个 16 位的二进制数,值为 1234H
DECIMAL_STR DB 5 DUP(0) ; 定义一个字符数组,用于存储转换后的十进制字符串,最多可存储 5 位十进制数
DATA ENDS
CODE SEGMENT
ASSUME CS:CODE, DS:DATA
START:
MOV AX, DATA ; 将数据段地址加载到 AX 寄存器
MOV DS, AX ; 将 AX 寄存器的值赋给数据段寄存器 DS,初始化数据段
MOV AX, BINARY_NUM ; 将二进制数加载到 AX 寄存器
MOV CX, 0 ; 初始化计数器 CX 为 0,用于记录转换后的十进制数的位数
CONVERT_LOOP:
MOV DX, 0 ; 将 DX 寄存器清零,用于存储除法运算的余数
MOV BX, 10 ; 将 BX 寄存器赋值为 10,作为除法运算的除数
DIV BX ; AX 除以 BX,商存于 AX,余数存于 DX
PUSH DX ; 将余数压入堆栈,保存起来
INC CX ; 计数器 CX 加 1,记录已处理的位数
CMP AX, 0 ; 比较 AX 是否为 0,判断是否所有位都已处理完
JNE CONVERT_LOOP ; 如果 AX 不为 0,继续循环处理下一位
PRINT_LOOP:
POP DX ; 从堆栈中弹出一个余数
ADD DL, 30H ; 将余数转换为对应的 ASCII 码字符(0 - 9 的 ASCII 码为 30H - 39H)
MOV AH, 2 ; 设置 DOS 功能调用号为 2,用于显示单个字符
INT 21H ; 执行 DOS 功能调用,显示字符
LOOP PRINT_LOOP ; 循环,直到所有余数都已弹出并显示
MOV AH, 4CH ; 设置 DOS 功能调用号为 4CH,用于程序结束并返回 DOS
INT 21H ; 执行 DOS 功能调用,结束程序并返回 DOS
CODE ENDS
END START
十进制转二进制
DATA SEGMENT
DECIMAL_NUM DB 123 ; 定义一个十进制数,值为 123
BINARY_RESULT DW? ; 定义一个 16 位的字,用于存储转换后的二进制数
DATA ENDS
CODE SEGMENT
ASSUME CS:CODE, DS:DATA
START:
MOV AX, DATA ; 将数据段地址加载到 AX 寄存器
MOV DS, AX ; 将 AX 寄存器的值赋给数据段寄存器 DS,初始化数据段
MOV AL, DECIMAL_NUM ; 将十进制数加载到 AL 寄存器
MOV AH, 0 ; 将 AH 寄存器清零,使 AX 寄存器成为一个 16 位寄存器,用于后续乘法运算
MOV BX, 1 ; 初始化 BX 寄存器为 1,作为乘法运算的初始权值
MOV BINARY_RESULT, 0 ; 初始化存储二进制结果的变量为 0
CONVERT_LOOP:
MOV DX, 0 ; 将 DX 寄存器清零,用于存储乘法运算的高 16 位结果
MOV CX, 10 ; 将 CX 寄存器赋值为 10,作为乘法运算的乘数
MUL CX ; AX 乘以 CX,结果的低 16 位存于 AX,高 16 位存于 DX
ADD AX, BX ; 将当前权值加到 AX 中,更新二进制结果
ADC DX, 0 ; 如果有进位,加到 DX 中
CMP AL, 0 ; 比较 AL 是否为 0,判断是否所有位都已处理完
JNE CONVERT_LOOP ; 如果 AL 不为 0,继续循环处理下一位
MOV BINARY_RESULT, AX ; 将最终的二进制结果存储到 BINARY_RESULT 变量中
MOV AH, 4CH ; 设置 DOS 功能调用号为 4CH,用于程序结束并返回 DOS
INT 21H ; 执行 DOS 功能调用,结束程序并返回 DOS
CODE ENDS
END START
二进制与 ASCII 码转换
二进制转 ASCII 码(单个字节处理)
DATA SEGMENT
BINARY_BYTE DB 10101010B ; 定义一个 8 位二进制数
ASCII_CHAR DB? ; 定义一个字符,用于存储转换后的 ASCII 码
DATA ENDS
CODE SEGMENT
ASSUME CS:CODE, DS:DATA
START:
MOV AX, DATA ; 将数据段地址加载到 AX 寄存器
MOV DS, AX ; 将 AX 寄存器的值赋给数据段寄存器 DS,初始化数据段
MOV AL, BINARY_BYTE ; 将二进制数加载到 AL 寄存器
CMP AL, 9 ; 比较 AL 中的值是否小于等于 9
JBE CONVERT_SINGLE ; 如果小于等于 9,直接转换为 ASCII 码
SUB AL, 10 ; 如果大于 9,减去 10,准备转换为十六进制对应的 ASCII 码(A - F)
ADD AL, 41H ; 加上 41H,得到 A - F 的 ASCII 码值
JMP STORE_RESULT ; 跳转到存储结果的位置
CONVERT_SINGLE:
ADD AL, 30H ; 如果小于等于 9,加上 30H,得到 0 - 9 的 ASCII 码值
STORE_RESULT:
MOV ASCII_CHAR, AL ; 将转换后的 ASCII 码存储到 ASCII_CHAR 变量中
MOV AH, 4CH ; 设置 DOS 功能调用号为 4CH,用于程序结束并返回 DOS
INT 21H ; 执行 DOS 功能调用,结束程序并返回 DOS
CODE ENDS
END START
第十章 列表处理
列表的基本操作 - 查找元素
DATA SEGMENT
LIST DW 10, 20, 30, 40, 50 ; 定义一个字类型的列表(数组)
LIST_SIZE EQU ($ - LIST) / 2 ; 计算列表元素的个数,每个元素为 2 字节
TARGET DW 30 ; 定义要查找的目标元素
RESULT DB? ; 定义一个字节变量,用于存储查找结果(找到为 1,未找到为 0)
DATA ENDS
CODE SEGMENT
ASSUME CS:CODE, DS:DATA
START:
MOV AX, DATA ; 将数据段地址加载到 AX 寄存器
MOV DS, AX ; 将 AX 寄存器的值赋给数据段寄存器 DS,初始化数据段
MOV CX, LIST_SIZE ; 将列表元素个数加载到 CX 寄存器,作为循环计数器
MOV SI, 0 ; 初始化索引寄存器 SI 为 0,用于遍历列表
SEARCH_LOOP:
MOV AX, LIST[SI] ; 将当前列表元素加载到 AX 寄存器
CMP AX, TARGET ; 比较当前元素与目标元素
JE FOUND ; 如果相等,跳转到找到的处理代码
ADD SI, 2 ; 索引 SI 增加 2,指向下一个列表元素(每个元素为 2 字节)
LOOP SEARCH_LOOP ; 循环,直到所有元素都已检查
MOV RESULT, 0 ; 如果未找到,将 RESULT 设为 0
JMP END_PROGRAM ; 跳转到程序结束部分
FOUND:
MOV RESULT, 1 ; 如果找到,将 RESULT 设为 1
END_PROGRAM:
MOV AH, 4CH ; 设置 DOS 功能调用号为 4CH,用于程序结束并返回 DOS
INT 21H ; 执行 DOS 功能调用,结束程序并返回 DOS
CODE ENDS
END START
列表的基本操作 - 插入元素
DATA SEGMENT
LIST DW 10, 20, 30, 40, 50, 0 ; 定义一个字类型的列表(数组),预留一个位置用于插入
LIST_SIZE EQU ($ - LIST) / 2 ; 计算列表元素的个数,每个元素为 2 字节
INSERT_VALUE DW 25 ; 定义要插入的元素值
INSERT_POSITION DB 2 ; 定义插入位置,从 0 开始计数
DATA ENDS
CODE SEGMENT
ASSUME CS:CODE, DS:DATA
START:
MOV AX, DATA ; 将数据段地址加载到 AX 寄存器
MOV DS, AX ; 将 AX 寄存器的值赋给数据段寄存器 DS,初始化数据段
MOV CX, LIST_SIZE - INSERT_POSITION ; 计算从插入位置到列表末尾的元素个数
MOV SI, (LIST_SIZE - 1) * 2 ; 初始化索引寄存器 SI 为列表最后一个元素的偏移地址
SHIFT_LOOP:
MOV AX, LIST[SI] ; 将当前元素加载到 AX 寄存器
MOV LIST[SI + 2], AX ; 将当前元素向后移动一个位置
SUB SI, 2 ; 索引 SI 减少 2,指向前一个元素
LOOP SHIFT_LOOP ; 循环,直到所有需要移动的元素都已移动
MOV AX, INSERT_VALUE ; 将插入值加载到 AX 寄存器
MOV LIST[INSERT_POSITION * 2], AX ; 将插入值插入到指定位置
MOV AH, 4CH ; 设置 DOS 功能调用号为 4CH,用于程序结束并返回 DOS
INT 21H ; 执行 DOS 功能调用,结束程序并返回 DOS
CODE ENDS
END START
列表的基本操作 - 删除元素
DATA SEGMENT
LIST DW 10, 20, 30, 40, 50 ; 定义一个字类型的列表(数组)
LIST_SIZE EQU ($ - LIST) / 2 ; 计算列表元素的个数,每个元素为 2 字节
DELETE_POSITION DB 2 ; 定义删除位置,从 0 开始计数
DATA ENDS
CODE SEGMENT
ASSUME CS:CODE, DS:DATA
START:
MOV AX, DATA ; 将数据段地址加载到 AX 寄存器
MOV DS, AX ; 将 AX 寄存器的值赋给数据段寄存器 DS,初始化数据段
MOV CX, LIST_SIZE - DELETE_POSITION - 1 ; 计算从删除位置后一个元素到列表末尾的元素个数
MOV SI, DELETE_POSITION * 2 ; 初始化索引寄存器 SI 为删除位置元素的偏移地址
SHIFT_LOOP:
MOV AX, LIST[SI + 2] ; 将下一个元素加载到 AX 寄存器
MOV LIST[SI], AX ; 将下一个元素向前移动一个位置
ADD SI, 2 ; 索引 SI 增加 2,指向下一个元素
LOOP SHIFT_LOOP ; 循环,直到所有需要移动的元素都已移动
MOV CX, 2 ; 将 CX 寄存器赋值为 2,用于调整列表大小(减少一个元素)
MOV SI, (LIST_SIZE - 1) * 2 ; 初始化索引寄存器 SI 为列表最后一个元素的偏移地址
CLEAR_LAST:
MOV WORD PTR LIST[SI], 0 ; 将列表最后一个位置清零,释放空间
SUB SI, 2 ; 索引 SI 减少 2,指向前一个元素
LOOP CLEAR_LAST ; 循环,直到最后一个元素被清零
MOV AH, 4CH ; 设置 DOS 功能调用号为 4CH,用于程序结束并返回 DOS
INT 21H ; 执行 DOS 功能调用,结束程序并返回 DOS
CODE ENDS
END START
以上代码详细展示了汇编语言中常见的代码转换和列表处理操作,通过注释希望能帮助你更好地理解每一步的操作意图。
标签:10,CODE,语言,MOV,汇编程序,寄存器,AX,DATA,LOOP From: https://blog.csdn.net/qq_40844444/article/details/144934589