最近课设在写汇编课程设计,题目也算是不经意间选了两个,并且两个题目之间也有相似部分,实现的方法也是类似的。
2019夏之汇编课程设计,保证代码,注释都开源。若有错误,欢迎指教。
课本:From 王爽老师
题目:快速排序+折半查找
一、快速排序
快速排序大家对算法都很熟悉,用汇编语言来实现主要是解决字符的输入输出,与有限的寄存器的矛盾。采用栈,缓冲区等方法来保存数据,增大保存信息的容量,不失为一种好方法。对于数据区的定义,存入,取出都是对CPU运作原理的理解与应用。
关于快速排序实现的思想,这篇博客给了我很多启发,Thanks,贴:大佬链接,对快拍的理解很深啊。
https://blog.csdn.net/man_sion/article/details/70138747
不只是标准思路,选第一个作为标准,一个方向:从前往后找一个 大于标准值的,找到先暂停,另一个方向,从后往前找一个小于标准值的,将这两个数字交换,如此继续,两个坐标相向而行,直到相遇,相遇后讲标准值和相遇点的值交换,从相遇点将数组看成两段,继续做快拍,所以快拍的时间复杂度为 Nlog2(N),每次都是二分的思想,所以是很快的。
但是假如用汇编实现,所需要的寄存器数量比较多,写起来肯定复杂,寄存器交作。所以采用下面这种两头交换法,二分分段的思想,实现,(两头交换法与标准算法思想的差异是,先从左边开始找到大于基准值的那个数,再从右边找到小于基准值的那个数,将两个数交换(这样比基准值小的都在左边,比基准值大的都在右边)。直到数列分成大于基准值和小于基准值的两个区间,以这两个区间进行同样的排序操作。)
汇编Code如下:
assume cs:code,ds:data
data segment
count dw 10
buffer db 20H dup(?) ; 例: 88,9,00,5,7,3,44,2,44,8
; (字节型能保存的最大数值是 0 -- 2^8-1,
; 但是为了简化输入和输出,只考虑了0-99,想做三位数后面的注释有提示 )
str0 db 0DH,0AH,24H ;回车,换行,结束
string db '--please input 10 numbers--',0DH,0AH,'$' ;,0DH,0AH,表示回车换行,'$'表示结束符
str1 db '--The 10 numbers Out as --',0DH,0AH,'$' ;,0DH,0AH,表示回车换行,'$'表示结束符
data ends
code segment
start:
mov ax,data
mov ds,ax
call show
call input
mov si,offset buffer
mov di,offset buffer
mov bx,offset buffer
add bx,count ;count 代表数据个个数
dec bx
call QSORT
call display
mov ah,4CH ;结束
int 21H
;-------------------------------------
;show函数
show proc near
mov dx,offset string
mov ah,09H
int 21H
ret
show endp
;---------------------------------------------------
;input函数
input proc near
lea dx,buffer
mov bx,dx
mov cx,count
AG1:
mov ah,01 ;处理十位 输入必须是两位数 例0要输入00
int 21H ;若是要改三位数,则此处的输入必须是三位数,
;然后逐位分离,数值累加到DH上,DH能接受的数值范围是 0-255
mov ah,10
sub al,'0'
mul ah
mov dh,al
mov ah,01 ;处理个位
int 21H
sub al,'0'
add dh,al
mov [bx],dh
inc bx
mov ah,01 ;处理空格
int 21H
loop AG1
mov dx,offset str0 ;换行
mov ah,09H
int 21H
ret
input endp
;----------------------------------------------------------------
;函数名:QSORT
;子程序功能:递归快速排序
;入口:DI,BX分别为起始和结束地址
;出口:内存中的数据已排序
QSORT PROC NEAR
PUSH DI
PUSH BX
CMP DI,BX
JNB NEXT ;不低于跳转=结束
PUSH DI
PUSH BX
CALL PARTITION
POP BX
POP DI
PUSH BX ; 因为在下次调用qsort时,需要BX的值不变
MOV BX,AX
DEC BX
PUSH DI
PUSH BX
CALL QSORT
POP BX
POP DI
POP BX ;对应上面的
MOV DI,AX
INC DI
PUSH DI
PUSH BX
CALL QSORT
POP BX
POP DI
NEXT:
POP BX
POP DI
RET
QSORT ENDP
;-------------------------------------------------------------
;子程序名:partition
;功能:进行一趟排序,将数据按枢轴分为两段
;入口:DI= &l[low], BX=&h[high]
;出口:返回基准轴位置在AX
PARTITION PROC NEAR
MOV CL,[DI] ;开始作为基准值
AG:
CMP DI,BX
JNB NEXT1 ;不低于跳转
Comeback:
CMP DI,BX
JNB ONE
CMP [BX],CL
JB ONE ;低于跳转
DEC BX
JMP Comeback
ONE:
MOV CH,[BX]
MOV AH,[DI]
MOV [DI],CH
MOV [BX],AH
Gohead:
CMP DI,BX
JNB TWO
CMP [DI],CL
JA TWO ;高于跳转
INC DI
JMP Gohead
TWO:
MOV CH,[BX]
MOV AH,[DI]
MOV [DI],CH
MOV [BX],AH
JMP AG
NEXT1:
MOV AX,DI ;基准轴地址给Ax
RET
PARTITION ENDP
;---------------------------------------------------------
;display函数
display proc near
mov dx,offset str0 ;输出换行
mov ah,09H
int 21H
mov dx,offset str1 ;'--The 10 numbers Out as --',0DH,0AH,'$'
mov ah,09H
int 21H
mov cx,count
mov si,offset buffer
AG2:
XOR AL,AL
ADD AL,[SI] ;这个是适用于个位数,十位数要跳转的
cmp al,10
JNB d2
d1: ;一位数
ADD AL,'0'
MOV DL,AL
INC SI
MOV AH,02
INT 21H
JMP AG3 ;为0即相等跳转
d2: ;两位数
mov ah,0
mov bl,10
div bl
mov dh,ah
mov dl,al
add dl,'0'
MOV AH,02
INT 21H
mov dl,dh
add dl,'0'
MOV AH,02
INT 21H
add si,1
jmp AG3
AG3: ;回到循环
MOV DL,' '
MOV AH,02
INT 21H
LOOP AG2
ret
display endp
code ends
end start
大部分都会汇编课上学到的指令,还有INT 21H中断的应用。INT 21H中断表在站内已经给出了很好的解释,还有JMP家族。
附带链接:
1:INT 21H中断表 https://blog.csdn.net/iDeal98/article/details/6567837
2:JMP家族表 https://blog.csdn.net/do2jiang/article/details/5262327
1.运行
2.运行解释(强行解释)
输入数据必须为两位数,因为涉及输入格式,处理空格操作,输入10个两位数,并且以空格等一个字符隔开,
回车就是结果,数值大小在(0-99)之间,输出数值的位数,和数值位数对应,输出结果从小往大排序。大部分解释都在代码里面。
感觉差不多了。
二、折半查找
折半查找也是熟悉算法,这里就解释下输入输出过程吧。
输入一个两位数,假如找到就,输出Y(Yes),并且输出数值所在数组的坐标位置。
假如没找到,就输出N(No),肯定不用输出坐标位置了。
然后输出这个数组。
1.汇编Code如下
assume cs:code,ds:data
data segment
number db 0,1,8,14,22,37,49,50,67,99
count dw 10
str0 db 0dh,0ah,24h
string db '--tell one number to find--',0DH,0AH,'$' ;,0DH,0AH,表示回车换行,'$'表示结束符
str2 db '-- the array as the down --',0DH,0AH,'$' ;,0DH,0AH,表示回车换行,'$'表示结束符
data ends
code segment
start:
mov ax,data
mov ds,ax
call show
call input
call finding ;找到之后输出坐标要加1
call display
mov ah,4cH
int 21H
;------------------------
;show函数
show proc near
mov dx,offset string
mov ah,09H
int 21H
ret
show endp
;-----------------------
;input函数
input proc near
mov ah,01 ;处理十位 输入必须是两位数 例0要输入00
int 21H
mov ah,10
sub al,'0'
mul ah
mov dh,al
mov ah,01 ;处理个位
int 21H
sub al,'0'
add dh,al
ret
input endp
;---------------------------
;finding函数
finding proc near
lea bx,number
mov cx,count
mov ax,0
F1:
cmp ax,cx
jg NotFind ;大于跳转
mov di,ax
add di,cx
sar di,1 ;di右移一位 变为一半
cmp dh,number[bx+di]
jz Finded ;为0跳转
cmp dh,number[bx+di]
jb NextF ;低于跳转
add di,1
mov ax,di
jmp F1
NextF:
dec di
mov cx,di
jmp F1
NotFind:
mov dx,offset str0
mov ah,09h
int 21H
mov dl,4eh ;78为N的ASCII码
jmp Out1
Finded:
mov dx,offset str0
mov ah,09h
int 21H
mov dl,59h ;89为Y的ASCII码
mov ah,2
int 21H
mov dl,' '
mov ah,2
int 21H
mov cx,di
mov dl,cl
cmp dl,09
jnb Out2 ;不低于跳转
inc dl
add dl,'0'
;inc dx ;输出的时候进行+1操作 还要判断这个数字是否大于10 >10跳转 Out2 ,<10顺序到Out1
Out1:
mov ah,2 ;打印输出(找到坐标10以下或者未找到)
int 21H
ret
Out2: ;打印输出(坐标大于10,为两位数)
mov dl,1
add dl,'0'
mov ah,2 ;打印输出
int 21H
mov dl,0
add dl,'0'
mov ah,2 ;打印输出
int 21H
ret
finding endp
;----------------------
;display函数
display proc near
mov dx,offset str0 ;输出换行
mov ah,09H
int 21H
mov dx,offset str2 ;输出'-- the array as the down --’
mov ah,09H
int 21H
mov cx,count
mov si,offset number
AG2:
XOR AL,AL
ADD AL,[SI] ;这个是适用于个位数,十位数要跳转的
cmp al,10
JNB d2
d1: ;一位数
ADD AL,'0'
MOV DL,AL
INC SI
MOV AH,02
INT 21H
JMP AG3 ;为0即相等跳转
d2: ;两位数
mov ah,0
mov bl,10
div bl
mov dh,ah
mov dl,al
add dl,'0'
MOV AH,02
INT 21H
mov dl,dh
add dl,'0'
MOV AH,02
INT 21H
add si,1
jmp AG3
AG3: ;回到循环
MOV DL,' '
MOV AH,02
INT 21H
LOOP AG2
ret
display endp
code ends
end start
2.运行解释
好吧大部分解释也在前面的叙述,和代码里面了,没有太多值得解释的。题目可能比同学的哈希简单多了。
贴图运行过程
折半查找 1没找到 17 ,2找到 0
好吧,也就这样吧。
三、收获之类的
在汇编里面实现排序算法,算法在数据结构和程序设计中已经学了,很熟悉了,在汇编中的难点就是,面对寄存器少,面对CPU,面对基础指令,实现数据转移,段跳转,子程序跳转。
字符处理:CPU寄存器保存的是 数值,而键盘输入的事ASCII码字符,所以输入输出之间的转换,
输入数值到寄存器中去,输入 ASCII码,保存的是数值,所以减去‘0’(0的ASCII码为 48)保存,
输出之前是数值,输出的事字符。所以加上‘0’,否则输入输出乱码,
多位数要逐位处理,乘法MUL,除法DIV的运用在输入,输出搭建实现。
INT 21H实现输入输出回显,处理空格,以AH为标志位,AL输入回显,DL输出字符,逐位输入,逐位输出。
MOV AH,4CH是 MOV AX,4C00H的有效成份。
四、感谢
感谢周学长(t,tkal),lixuezhang,pengxuezhang……等等不能列举。