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