目录
前言
在前面两篇文章中,对单片机有了初步的了解,下面将介绍利用汇编语言实现冒泡排序。
一、本篇文章将使用的指令介绍
MOV 传送字或字节
INC 加1,类比与C语言++
DEC 减1,类比与C语言--
JC 有进位/借位时转移,cy端为进位/借位标志位;当进位/借位时cy为1
CJNE 比较不等转移指令
DJNZ 减一不为0跳转指令
SJMP 短跳转指令
CLR 清零指令
JB 无符号小于则跳转
XCH 交换两个操作数的值指令
SETB 置1
二、交换数值的实现
2.1 XCH 两个操作数的数值交换
交换两个操作数的值一般用XCH指令实现,下面是该指令是使用格式:
XCH的使用格式: XCH A,SRC(SRC为源操作数)
观察格式发现第一个操作数只能是累加器A,那么如果要实现R0跟R7内是数值交换怎么做呢?由于第一个操作数被限定为A,那么就需要将R0/R7的其中一个数值给A,再与另外一个数进行交换;而给A数值的R7/R0需要再被A交换或者传数值,代码实现如下:
MOV R0,#5 ;XCH function:exhange the vail between two number
MOV R7,#45
MOV A,R0
XCH A,R7
MOV R0,A ;XCH A,R0
END ;结束标志符
执行XCH指令后的结果为:
此时可以发现仅仅A与R7的数值进行了交换,而R0几乎没变。这是因为仅仅对R0进行了传送数据的指令操作,而没有对R0进行另外的操作,所以在第五行需要对R0进行操作,如果执行第五行——MOV R0,A 的结果为:
而如果执行——XCH A,R0,结果为:
从结果来看,MOV R0,A或者XCH A,R0的最后目的都达到了,不同的区别点就是程序结束后,累加器A的数值不一样。在具体功能的实现下这两者需要加以区分。
看完XCH的数值交换实现后,你可能会想如果不用XCH能不能实现两个操作数的数值交换呢?结果是肯定的。那么该如何实现呢,这其实与酱油,醋瓶的交换类似。设想一下,你面前由一个红色瓶装醋,一个蓝色瓶装酱油,如果要求将酱油装如红色瓶,醋装入蓝色瓶,该如何操作呢?
没错,就是在用一个空瓶作为中介,借助它实现酱油,醋瓶的交换。由此,可以实现30H单元与50H 单元的数值交换,以60H单元作为中介,代码如下:
MOV 30H,#30H function:exhange the vail between two number
MOV 50H,#20H
MOV 60H,30H
MOV 30H,50H
MOV 50H,60H
END
结果如下:
所以实现目的方法有多种,并非唯一的,选择适合自己的方法就行。
2.2 XCHD 低4位交换
交换两个操作数的值的低4位一般用XCHD指令实现,下面是该指令是使用格式:
XCHD A,@Ri |
其中第一个操作数只能是累加器A,第二个操作数只能为寄存器间接寻址。下面实现33H与R7的低四位交换,代码如下:
MOV R0,#33H
MOV 33H,#66H
MOV R7,#22H
XCH A,R7
;XCH R1,R0
XCHD A,@R0
MOV R7,A
END
因为指令格式的限定,需要进行传入数据MOV 的操作,初始下33H单元的数值位O*66H,寄存器R7的数值为O*22H,交换低4位后33H单元的数值位O*62H,寄存器R7的数值为O*26H,结果如图所示:
2.3 SWAP 交换高低次位
交换高低次位的指令为SWAP,是应该单目操作符,下面是该指令是使用格式:
SWAP A |
下面是实现交换30H单元数据的高低次伟,代码如下:
MOV 30H,#45H
MOV A,30H
SWAP A
MOV 30H,A
END
结果由O*45H——>O*54H,如图所示:
三、冒泡排序——汇编版本
3.1 小——>大的冒泡排序
小——>大的冒泡排序,将两个数进行比较,将大数放后,经过第一趟冒泡排序后,最大的数值便放在了最后,比较次数减一,直到所有所给的数据满足小——>大的顺序时,排序结束。简而言之就是,不断的把大数放后,直到只剩一个数时,排序完成。
接下来就是具体细节的分析,
- 交换两个数时使用XCH,需要用到累加器A,那么得把一个数值传入A,并且对操作数的操作能够直接影响原始数据,这里得用到寄存器间接寻址
- 如何判断数据的排列顺序是否符合小——>大的顺序?这里得用标志位F0进行判断,如果前后两个数的顺序不符合小——>大的顺序,F0为1,即SETB F0;前后两个数的顺序符合小——>大的顺序,F0为0,即CLR F0。
- 比较次数如何设定?由原始数据的个数-1开始——第一个数不用比较,毕竟自己跟自己比无大小之分,比较无意义。在经历一趟冒泡排序后,次数继续减一。
这里是10个数的小——>大,冒泡排序,需要比较更多数的话修改41H的源操作数,但不能超过O*FFH,代码如下:
;小——>大冒泡排序
MOV 41H,#10 ;41H存的为排序数字的个数
START:CLR F0 ;F0作为顺序是否一致的标志位
MOV R7,41H ;R7作为循环次数
DEC R7 ;第一个数不用比较,次数减一
MOV R0,#30H ;将30日的首地址给R0
MOV 40H,@R0 ;将R0指向的地址元素给40H,40H作为前数
MAOP_1:INC R0 ;将下一个元素送给R0
MOV A,@R0 ;A作为后数
CJNE A,40H,NEXT
NEXT:JC EXC ;有进位,说明前数比后数大,跳转EC交换程序
XCH A,40H ;否则,将后数给40H,作为前数
SJMP CONT
EXC:SETB F0 ;F0作为表示为,即顺序不满足,小->大
XCH A,40H ;前数与后数交换,此时40H为小数,A为大数
MOV @R0,A ;R0此时指向后数,将大数放后,小数提前,完成两数排序
DEC R0 ;R0指向前数
XCH A,40H ;保证40日单元始终为大数
MOV @R0,A
INC R0
CONT:CLR C
DJNZ R7,MAOP_1
DEC 41H ;循环一次大数都放到最后,循环次数减一,减小遍历次数
JB F0,START ;检查F0顺序,即n个数是否满足小->大
END
初始数据的输入,keil的数据输入与vs的输入不同,vs可直接用scanf函数继续数据的输入,而keil得在memory窗口输入,如图所示:
输入的初始10个数据为: 5 8 1 2 3 41 21 23 7 63。经过第一趟冒泡排序后,结果应为:5 1 2 3 8 21 23 7 41 63,运行程序观察第一趟冒泡排序结果为:
经过第二趟冒泡排序后,结果应为:1 2 3 5 8 21 7 23 41 63,运行程序观察第二趟冒泡排序结果为:
经过第三趟冒泡排序后,结果应为:1 2 3 5 8 7 21 23 41 63,运行程序观察第三趟冒泡排序结果为:
经过第三趟冒泡排序后,结果应为:1 2 3 5 7 8 21 23 41 63,运行程序观察第三趟冒泡排序结果为:
至此冒泡排序结束,且符合预期结果,说明程序的逻辑与功能实现符合预期,是可行的。
3.2 大——>小的冒泡排序
下面介绍大——>小的冒泡排序。可以说大——>小的冒泡排序与小——>大的冒泡排序几乎相近,只不过往最后输送的数是大数还是小数。
接下来就是具体细节的分析,
- 交换两个数时使用XCH,需要用到累加器A,那么得把一个数值传入A,并且对操作数的操作能够直接影响原始数据,这里得用到寄存器间接寻址
- 如何判断数据的排列顺序是否符合大——>小的顺序?这里得用标志位F0进行判断,如果前后两个数的顺序不符合大——>小的顺序,F0为1,即SETB F0;前后两个数的顺序符合小——>大的顺序,F0为0,即CLR F0。
- 比较次数如何设定?由原始数据的个数-1开始——第一个数不用比较,毕竟自己跟自己比无大小之分,比较无意义。在经历一趟冒泡排序后,次数继续减一。
这里是10个数的大——>小,冒泡排序,代码如下:
;大——>小
MOV 41H,#10 ;41H存的为排序数字的个数
START:CLR F0 ;F0作为顺序是否一致的标志位
MOV R7,41H ;R7作为循环次数
DEC R7 ;第一个数不用比较,次数减一
MOV R0,#30H ;将30日的首地址给R0
MOV 40H,@R0 ;将R0指向的地址元素给40日,40日作为前数
MAOP_1:INC R0 ;将下一个元素送给R0
MOV A,@R0 ;A作为后数
CJNE A,40H,NEXT
NEXT:JNC EXC ;没有进位,说明前数比后数小,跳转EC交换程序
XCH A,40H ;否则,将后数给40日,作为前数
SJMP CONT
EXC:SETB F0 ;F0作为表示为,即顺序不满足,大->小
XCH A,40H ;前数与后数交换,此时40H为大数,A为小数
MOV @R0,A ;R0此时指向后数,将小数放后,大数放前,实现大->小
DEC R0 ;R0指向前数
XCH A,40H ;保证40日单元始终为小数
MOV @R0,A
INC R0
CONT:CLR C
DJNZ R7,MAOP_1
DEC 41H ;循环一次小数都放到最后,循环次数减一,减小遍历次数
JB F0,START ;检查F0顺序,即n个数是否满足大->小
END
这里继续用上面的10个数据: 5 8 1 2 3 41 21 23 7 63。这里不继续展示每趟冒泡排序的结果,直接贴最后结果,如图所示:
至此大——>小的冒泡排序完成。
总结
本文仅仅简单介绍了汇编的冒泡排序,对冒泡排序进行了粗略的分析,具体的细节需要自行感受体会。