8086汇编实现冒泡排序、直接插入排序、折半查找
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
MOV MIN,BYTE PTR[BX] MOV MAX, BYTE PTR[BX+99]
这是编译错误,而不是逻辑错误。因为 CPU 指令系统的 MOV 指令要求其两个操作数不能同 时为存储器操作数。这是初学者经常会犯的错误。
下面将介绍折半查找。 例二:在内存 Score 缓冲区中存放有 100 个学生的成绩数据,为无符号字节数。设计程序完 成如下功能:
L3: MOV DH,AL
; a[i]—>a[0]
MOV AL,DL
; a[i-1]—>a[i],虚假的,只是在寄存器中
MOV [BX+SI],AL ; a[i-1]—>a[i],真正的,在内存中
MOV DI,SI
; DI 相当于 j,i—>j
SUB DI,2
; j-2—>j
JS L5
;这一步很重要,防止数组越界
INT 21H 输出的字符需要先存在 DL 中。
折半查找:以处于区间中间位置的数据和给定值比较,若相等,则查找成功;若不等,则缩 小范围,直到新的区间中间位置的数据等于给定值或查找区间的大小小于零是为止。
首先介绍 C 语言版的折半查找: int search(int key) {
low=1; high=100; while(low<=high) {
LOOP L1
L6: MOV AL,BYTE PTR[BX]
MOV AH,BYTE PTR[BX+99]
MOV MAX,AH
MOV MIN,AL
MOV AH,4CH ;返回操作系统
INT 21H
CSEG ENDS
END START
可以看出,上述代码的核心就是 C 代码的汇编语言翻译。 需注意:
① 最后将最大值和最小值分别存入 MAX、MIN 单元中时,不能直接从数组的存储单元存入 MAX 和 MIN 中,如下面的语句是错误的:
mid=(low+high)/2; if(a[mid]==key) return mid; else if(key<a[mid]) high=mid-1; else low=mid+1; } return 0; }
下面是折半查找的汇编语言版: DSEG SEGMENT
SCORE DB 1H,2H,3H,4H,5H,6H,7H,8H,90 DUP(12H),13H,14H DSEG ENDS CSEG SEGMENT
(2)直接插入法 直接插入排序:将一个数据插入到一个已排好序的数据中,主要步骤如下:
① 查找应该插入的位置,这个过程免不了要比较数据的大小; ② 找到位置后,需将这个位置以及其后的数据都向后移动一位,空出此位置,等待插入 ③ 插入数据。
其 C 语言版的代码如下:
for(int i=2;i<=100;i++)
利用汇编语言实现冒泡排序、直接插入排序、折半查找。 下面以例题的形式进行讲解。
例一:在内存 Score 缓冲区中存放有 100 个学生的成绩数据,为无符号字节数。 要求:
1、 用冒泡排序对数据从大到小排序; 2、 直接插入排序对数据从小到大排序; 3、 将最高分和最低分分别放在 MIN 和 MAX 单元中。
Biblioteka Baidu;相当于 i
L2: MOV AL,[BX+DI] ; L2 为内循环,(DI)为循环变量,相当于 j
CMP AH,AL
JAE L3
MOV DH,AH
;AH<Al,交换两个数
MOV AH,AL
MOV AL,DH
MOV [BX+DI],AL ;将交换后的数存入存储器
MOV [BX+SI],AH ;这两步很重要
if(a[i]<a[i-1])
;找到位置
{
a[0]=a[i];
a[i]=a[i-1];
for(int j=i-2;a[0]<a[j];j--)
a[j+1]=a[j];
;向后移
a[j+1]=a[0];
;插入数据
}
其中 a[0]不存放数据,充当转换的中间容器。数据从 a[1]—a[100]。
按照上面的代码将其转换成汇编版的插入排序法:
要注意的几点: ① 数据段使用的 DUP 相当于 C 语言中的数组,可以很简便的输入很多数据,即使这些数据
都是相同的,但不影响对程序的测试。 ② 在寄存器间接寻址、基址寻址、变址寻址、基变址寻址中,注意下面:
A、中括号[]中只能是 BX,SI,DI 三种寄存器,而不能加入其他的寄存器,如“MOV AX,[BX+DX]”是错误的;
根据用户输入的一个 2 位十进制数,作为查找对象,在该数组中查找,若找到则显示 “Y”,若没找到则显示“N”。
思路: 输入:MOV AH,01H
INT 21H 一次只能输入一个字符,且字符的 ASCII 码值存放在 AL 中。要输入一个两位数,就需要连 续输入两个字符并将其转换成十进制数(因为分数是十进制数)。 输出:MOV AH,02H
L3: INC DI
;AH>=AL,不需交换,(AH)直接和后一个数比较,相当于 j++
CMP DI,100 ;判断内层循环是否结束
JB L2
;没结束,继续循环
;内层循环结束了
INC SI
;外层变量 SI 加一,相当于 i++
MOV DI,SI ;相当于 j=i
LOOP L1
;通过寄存器实现两个存储器数据间的交换 MOV AH,BYTE PTR[BX] ;基址寻址 MOV AL,BYTE PTR[BX+99] MOV MAX,AH MOV MIN,AL MOV AH,4CH ;返回操作系统 INT 21H CSEG ENDS END START
L4: MOV DL,[BX+DI] ;从内存中取出 a[j],给(DL)
CMP DH,DL
;比较 a[0]和 a[j]
JAE L5
;a[0]<a[j],向后移
MOV [BX+DI+1],DL ;存入内存,实现真正的后移
DEC DI
;j--
JMP L4
L5: MOV [BX+DI+1],DH ;a[0]>=a[j],a[0]—>a[j+1],实现直接插入
那么,用汇编语言怎么来实现呢?其实道理很简单,就是按照 C 语言的形式将其转换成汇编 语言。当然,还有其他的一些操作需要处理,如数据段„„
下面是汇编语言的冒泡排序:
DSEG SEGMENT SCORE DB 11H,02H,15H,32H,5H,6H,7H,8H,9H,10H,90 DUP(05H)
其实软件开发中,在进行代码编写之前都需要画流程图、N—S 流程图等等,所以我们 得养成先画流程图再写程序的习惯。
MOV L1: INC L2: MOV
MOV CMP JAE
CX,99 ;设置循环次数,注意不是 100,这主要由程序的写法决定的
SI
; i++
AL,[BX+SI]
; a[i]—>(AL)
DL,[BX+SI-1] ; a[i-1]—>(DL)
AL,DL
L1
;a[i] >=a[i-1]
;a[i] < a[i-1]
Ax,Ax ;min 初始值为 0 CH,CH ;清零
AL,CL ;注意不能将“JG”写成“JA”,即应该是带符号数比较 ;否则当你输入“00”时,将不输出结果
MOV ADD SAR
DI,AX DI,CX DI,1
;实现 mid=(max+min)/2
CMP JZ L4 CMP JB L2 INC DI MOV JMP
DH,BYTE PTR[BX+DI] ;找到 DH,BYTE PTR[BX+DI]
;min=mid+1 AX,DI L1
;key 和 a[mid]的比较 ;key<mid
L2: DEC MOV JMP
L3: MOV JMP
L4: MOV
DI ;max=mid-1 CX,DI L1 DL,'N' ;存入 DL,以待输出 L5 DL,'Y'
解: (1)冒泡法
什么叫冒泡法呢?估计大家已经非常熟悉,在 C 语言中经常用到。用 C 语言实现冒泡 的代码如下:
for(int i=0;i<100;i++) for(int j=i+1;j<100;j++) { if(a[i]>a[j]) { int t=a[i]; a[i]=a[j]; a[j]=t; } }
DSEG SEGMENT SCORE DB 31H,02H,15H,4H,5H,6H,7H,8H,9H,10H,90 DUP(05H) MAX DB ? MIN DB ?
DSEG ENDS CSEG SEGMENT
ASSUME DS:DSEG,CS:CSEG START: MOV AX,DSEG
MOV DS,AX
B、而且[]中也不能同时出现两个变址寄存器 SI 和 DI,如“MOV AX,[SI+DI+3] ” 是 错 误 的; C、如果在[]内加入了存储器,如“MOV AX,[BX+MAX]”,则此时的 MAX 代表着偏移地
址,而不是 MAX 所对应的数据的值。 ③ 在交换数据时,不仅要交换寄存器中的数据,而且要将存储器中的数据交换过来。
;输入第二个字符 ;最后的十进制数存放在 DH 中
MOV INT
AH,1 21H
;接收回车符<enter>
MOV MOV INT
DL,10 AH,2 21H
;输出换行
LEA MOV
BX,SCORE ;数组的首地址放在 BX 中 CL,99 ;max 初始值为 99
XOR XOR L1: CMP JG L3
L5: MOV INT
AH,2 21H
;输出结果
MOV AH,4CH
INT
21H
CSEG ENDS
END START
;返回操作系统
注意输入输出格式的控制。
总结:从以上的程序编辑过程来看,只要能将算法的 C 语言代码写出来,就能够将相应的汇 编程序写出来。当然你可能觉得这是不是有点麻烦——在写汇编程序之前还必须将相应的 C 语言程序写出来,其实这是编写汇编程序的初级阶段,等你熟悉了汇编以后,只要你知道算 法的思想,就能够跳过 C 代码的编写,直接进入汇编程序的编写。
MAX DB ? MIN DB ? DSEG ENDS CSEG SEGMENT ASSUME DS:DSEG,CS:CSEG START: MOV AX,DSEG MOV DS,AX ;————————————————到这,上面的均是模板 LEA BX,SCORE ;取数组的首地址 MOV CX,100 ;控制循环次数 XOR SI,SI ;将 SI 清零 XOR DI,DI ;将 DI 清零 L1: MOV AH,[BX+SI] ;用基变址寻址取操作数, L1 为外循环,(SI)为循环变量,
ASSUME DS:DSEG,CS:CSEG START: MOV AX,DSEG
MOV DS,AX
MOV INT MOV SUB MUL MOV MOV INT SUB ADD
AH,1 21H AH,10 AL,'0' AH DH,AL AH,1 21H AL,'0' DH,AL
;输入第一个字符 ;十进制数
这是编译错误,而不是逻辑错误。因为 CPU 指令系统的 MOV 指令要求其两个操作数不能同 时为存储器操作数。这是初学者经常会犯的错误。
下面将介绍折半查找。 例二:在内存 Score 缓冲区中存放有 100 个学生的成绩数据,为无符号字节数。设计程序完 成如下功能:
L3: MOV DH,AL
; a[i]—>a[0]
MOV AL,DL
; a[i-1]—>a[i],虚假的,只是在寄存器中
MOV [BX+SI],AL ; a[i-1]—>a[i],真正的,在内存中
MOV DI,SI
; DI 相当于 j,i—>j
SUB DI,2
; j-2—>j
JS L5
;这一步很重要,防止数组越界
INT 21H 输出的字符需要先存在 DL 中。
折半查找:以处于区间中间位置的数据和给定值比较,若相等,则查找成功;若不等,则缩 小范围,直到新的区间中间位置的数据等于给定值或查找区间的大小小于零是为止。
首先介绍 C 语言版的折半查找: int search(int key) {
low=1; high=100; while(low<=high) {
LOOP L1
L6: MOV AL,BYTE PTR[BX]
MOV AH,BYTE PTR[BX+99]
MOV MAX,AH
MOV MIN,AL
MOV AH,4CH ;返回操作系统
INT 21H
CSEG ENDS
END START
可以看出,上述代码的核心就是 C 代码的汇编语言翻译。 需注意:
① 最后将最大值和最小值分别存入 MAX、MIN 单元中时,不能直接从数组的存储单元存入 MAX 和 MIN 中,如下面的语句是错误的:
mid=(low+high)/2; if(a[mid]==key) return mid; else if(key<a[mid]) high=mid-1; else low=mid+1; } return 0; }
下面是折半查找的汇编语言版: DSEG SEGMENT
SCORE DB 1H,2H,3H,4H,5H,6H,7H,8H,90 DUP(12H),13H,14H DSEG ENDS CSEG SEGMENT
(2)直接插入法 直接插入排序:将一个数据插入到一个已排好序的数据中,主要步骤如下:
① 查找应该插入的位置,这个过程免不了要比较数据的大小; ② 找到位置后,需将这个位置以及其后的数据都向后移动一位,空出此位置,等待插入 ③ 插入数据。
其 C 语言版的代码如下:
for(int i=2;i<=100;i++)
利用汇编语言实现冒泡排序、直接插入排序、折半查找。 下面以例题的形式进行讲解。
例一:在内存 Score 缓冲区中存放有 100 个学生的成绩数据,为无符号字节数。 要求:
1、 用冒泡排序对数据从大到小排序; 2、 直接插入排序对数据从小到大排序; 3、 将最高分和最低分分别放在 MIN 和 MAX 单元中。
Biblioteka Baidu;相当于 i
L2: MOV AL,[BX+DI] ; L2 为内循环,(DI)为循环变量,相当于 j
CMP AH,AL
JAE L3
MOV DH,AH
;AH<Al,交换两个数
MOV AH,AL
MOV AL,DH
MOV [BX+DI],AL ;将交换后的数存入存储器
MOV [BX+SI],AH ;这两步很重要
if(a[i]<a[i-1])
;找到位置
{
a[0]=a[i];
a[i]=a[i-1];
for(int j=i-2;a[0]<a[j];j--)
a[j+1]=a[j];
;向后移
a[j+1]=a[0];
;插入数据
}
其中 a[0]不存放数据,充当转换的中间容器。数据从 a[1]—a[100]。
按照上面的代码将其转换成汇编版的插入排序法:
要注意的几点: ① 数据段使用的 DUP 相当于 C 语言中的数组,可以很简便的输入很多数据,即使这些数据
都是相同的,但不影响对程序的测试。 ② 在寄存器间接寻址、基址寻址、变址寻址、基变址寻址中,注意下面:
A、中括号[]中只能是 BX,SI,DI 三种寄存器,而不能加入其他的寄存器,如“MOV AX,[BX+DX]”是错误的;
根据用户输入的一个 2 位十进制数,作为查找对象,在该数组中查找,若找到则显示 “Y”,若没找到则显示“N”。
思路: 输入:MOV AH,01H
INT 21H 一次只能输入一个字符,且字符的 ASCII 码值存放在 AL 中。要输入一个两位数,就需要连 续输入两个字符并将其转换成十进制数(因为分数是十进制数)。 输出:MOV AH,02H
L3: INC DI
;AH>=AL,不需交换,(AH)直接和后一个数比较,相当于 j++
CMP DI,100 ;判断内层循环是否结束
JB L2
;没结束,继续循环
;内层循环结束了
INC SI
;外层变量 SI 加一,相当于 i++
MOV DI,SI ;相当于 j=i
LOOP L1
;通过寄存器实现两个存储器数据间的交换 MOV AH,BYTE PTR[BX] ;基址寻址 MOV AL,BYTE PTR[BX+99] MOV MAX,AH MOV MIN,AL MOV AH,4CH ;返回操作系统 INT 21H CSEG ENDS END START
L4: MOV DL,[BX+DI] ;从内存中取出 a[j],给(DL)
CMP DH,DL
;比较 a[0]和 a[j]
JAE L5
;a[0]<a[j],向后移
MOV [BX+DI+1],DL ;存入内存,实现真正的后移
DEC DI
;j--
JMP L4
L5: MOV [BX+DI+1],DH ;a[0]>=a[j],a[0]—>a[j+1],实现直接插入
那么,用汇编语言怎么来实现呢?其实道理很简单,就是按照 C 语言的形式将其转换成汇编 语言。当然,还有其他的一些操作需要处理,如数据段„„
下面是汇编语言的冒泡排序:
DSEG SEGMENT SCORE DB 11H,02H,15H,32H,5H,6H,7H,8H,9H,10H,90 DUP(05H)
其实软件开发中,在进行代码编写之前都需要画流程图、N—S 流程图等等,所以我们 得养成先画流程图再写程序的习惯。
MOV L1: INC L2: MOV
MOV CMP JAE
CX,99 ;设置循环次数,注意不是 100,这主要由程序的写法决定的
SI
; i++
AL,[BX+SI]
; a[i]—>(AL)
DL,[BX+SI-1] ; a[i-1]—>(DL)
AL,DL
L1
;a[i] >=a[i-1]
;a[i] < a[i-1]
Ax,Ax ;min 初始值为 0 CH,CH ;清零
AL,CL ;注意不能将“JG”写成“JA”,即应该是带符号数比较 ;否则当你输入“00”时,将不输出结果
MOV ADD SAR
DI,AX DI,CX DI,1
;实现 mid=(max+min)/2
CMP JZ L4 CMP JB L2 INC DI MOV JMP
DH,BYTE PTR[BX+DI] ;找到 DH,BYTE PTR[BX+DI]
;min=mid+1 AX,DI L1
;key 和 a[mid]的比较 ;key<mid
L2: DEC MOV JMP
L3: MOV JMP
L4: MOV
DI ;max=mid-1 CX,DI L1 DL,'N' ;存入 DL,以待输出 L5 DL,'Y'
解: (1)冒泡法
什么叫冒泡法呢?估计大家已经非常熟悉,在 C 语言中经常用到。用 C 语言实现冒泡 的代码如下:
for(int i=0;i<100;i++) for(int j=i+1;j<100;j++) { if(a[i]>a[j]) { int t=a[i]; a[i]=a[j]; a[j]=t; } }
DSEG SEGMENT SCORE DB 31H,02H,15H,4H,5H,6H,7H,8H,9H,10H,90 DUP(05H) MAX DB ? MIN DB ?
DSEG ENDS CSEG SEGMENT
ASSUME DS:DSEG,CS:CSEG START: MOV AX,DSEG
MOV DS,AX
B、而且[]中也不能同时出现两个变址寄存器 SI 和 DI,如“MOV AX,[SI+DI+3] ” 是 错 误 的; C、如果在[]内加入了存储器,如“MOV AX,[BX+MAX]”,则此时的 MAX 代表着偏移地
址,而不是 MAX 所对应的数据的值。 ③ 在交换数据时,不仅要交换寄存器中的数据,而且要将存储器中的数据交换过来。
;输入第二个字符 ;最后的十进制数存放在 DH 中
MOV INT
AH,1 21H
;接收回车符<enter>
MOV MOV INT
DL,10 AH,2 21H
;输出换行
LEA MOV
BX,SCORE ;数组的首地址放在 BX 中 CL,99 ;max 初始值为 99
XOR XOR L1: CMP JG L3
L5: MOV INT
AH,2 21H
;输出结果
MOV AH,4CH
INT
21H
CSEG ENDS
END START
;返回操作系统
注意输入输出格式的控制。
总结:从以上的程序编辑过程来看,只要能将算法的 C 语言代码写出来,就能够将相应的汇 编程序写出来。当然你可能觉得这是不是有点麻烦——在写汇编程序之前还必须将相应的 C 语言程序写出来,其实这是编写汇编程序的初级阶段,等你熟悉了汇编以后,只要你知道算 法的思想,就能够跳过 C 代码的编写,直接进入汇编程序的编写。
MAX DB ? MIN DB ? DSEG ENDS CSEG SEGMENT ASSUME DS:DSEG,CS:CSEG START: MOV AX,DSEG MOV DS,AX ;————————————————到这,上面的均是模板 LEA BX,SCORE ;取数组的首地址 MOV CX,100 ;控制循环次数 XOR SI,SI ;将 SI 清零 XOR DI,DI ;将 DI 清零 L1: MOV AH,[BX+SI] ;用基变址寻址取操作数, L1 为外循环,(SI)为循环变量,
ASSUME DS:DSEG,CS:CSEG START: MOV AX,DSEG
MOV DS,AX
MOV INT MOV SUB MUL MOV MOV INT SUB ADD
AH,1 21H AH,10 AL,'0' AH DH,AL AH,1 21H AL,'0' DH,AL
;输入第一个字符 ;十进制数