实验-冒泡排序算法的实现(汇编语言与接口技术)
用汇编实现冒泡排序算法。在上一个实验(循环程序的设计)的代码基础上,利用冒泡排序将DEST中数据从小到大重新排序。将冒泡排序算法写成子程序。在主程序中,通过设置参数,使用同一个子程序实现从大到小,从小到大排序的自由选择。在main里面设置7AH地址的值,调用子程序来进行冒泡排序进行现场保护进入冒泡排序,根据7AH地址的值来判断是从小到大排序还是从大到小排序。每次取DEST(DPTR指向此地址)为起
一、 实验要求
- 用汇编实现冒泡排序算法。
- 在上一个实验(循环程序的设计)的代码基础上,利用冒泡排序将DEST中数据从小到大重新排序 。
- 将冒泡排序算法写成子程序。
- 在主程序中,通过设置参数,使用同一个子程序实现从大到小,从小到大排序的自由选择。
二、 实验设计
2.1 整体思路
-
在main里面设置7AH地址的值,调用子程序来进行冒泡排序
-
进行现场保护
-
进入冒泡排序,根据7AH地址的值来判断是从小到大排序还是从大到小排序。
-
每次取DEST(DPTR指向此地址)为起始地址的数组值,两两比较排序,直到所有的数据都排序完成。
-
进行现场保护
-
结束排序,返回main。
2.2 流程图

上流程图省略了从小到大,其过程与从大到小差不多。
2.3 主要模块设计思路及分析
(1)冒泡排序算法模块(这里以从大到小排序为例)
冒泡算法的的思路(这里以从大到小排序为例)为:
在一组数据中,从第一个数据开始,两两进行比较,如果前者较后者小,则交换他们的位置,如果前者较后者大,则进行下一组的比较。这样每一轮排序之后,我们都可以将一组数据中最小的数据移到最后。下一轮对比的数据则可以去除上一轮的最小数据(每一轮之后,对比的数据减1)。直到所有的数据对比完毕。
在这个程序中,我们需要设计两个循环(外循环与内循环)。
外循环控制整个排序过程需要进行的遍历次数。
对于一个长度为 n 的数组,最多需要进行 n-1 次遍历(因为每完成一次遍历,最小的元素就会被移动到数组的末尾,因此下一次遍历不需要再考虑这个元素)。所以在这里,外循环的初始值为num-1。每循环一次,次数减1。
内循环负责比较并交换相邻的元素。
内循环的起始索引从数组的第一个元素开始,而终止索引则随着外循环的进行逐渐减小(因为每次外循环后,末尾的元素已经排好序,不需要再次比较)。
所以在这里,内循环的初始值为当前外循环次数的值。每循环一次,次数减1。当内循环结束 后,内循环次数再次赋值为当前外循环次数的值。
(2)利用DPTR取数据模块
首先将DPTE指向DEST所在的地址。每次需要取两个数据放到两个寄存器中,所以每一次取数据结束后DPTR会指向后一个数据的地址。但是如果这两个数据需要交换的话,则需要先将当前DPTR所指的地址数据改为前一个数据(利用暂存值的A寄存器来实现),再将DPTR的值减一(通过DEC DPL来解决,因为这里不会借位,所以不考虑借位的情况),来将后一个数据写到前一个数据的地址。每进行一轮内循环,DPTR值加一。每进行一次外循环,DPTE重新指向DEST所在的地址。
三、 实现效果
a.从小到大排序:
(1)未排序前:

如图可以看到:
在实验三中我们将数据拷贝到2000H为起始地址的RAM片外区。并且将字符‘$’存到最后。
(2)排序后:

如图可以看到:
数据从小到大排序后保存到2000H的起始地址中。
b.从大到小:
(1)未排序前:

如图可以看到:
数据还保存着上一步的从小到大的排序。
(2)排序后:

如图可以看到:
数据从大到小排序后保存到2000H的起始地址中。
四、源代码
//1.用汇编实现冒泡排序算法。
//2.在实验三的代码基础上,利用冒泡排序将DEST中数据从小到大重新排序 。
//3.将冒泡排序算法写成子程序。在主程序中,通过设置参数,使用同一个子程序实现从大到小,从小到大排序的自由选择。
ORG 0000H
LJMP MAIN
ORG 2000H
MAIN:
SRC DATA 30H
DEST XDATA 2000H
NUM DATA 20H
MOV A, #0AH ; 将立即数0AH加载到累加器A中 ,为需要循环的次数
MOV R0, #NUM ; 将NUM(实际上是20H)加载到寄存器R0中
MOV @R0, A
ACALL WRITE_TO_SRC ; 调用写入子程序
ACALL COPY_TO_DEST ; 调用复制子程序
// 进入冒泡排序
CLR 7AH ; 从小到大排序
ACALL ORDERS ; 冒泡排序子程序
SETB 7AH ; 从大到小排序
ACALL ORDERS ; 冒泡排序子程序
SJMP $
// 子程序:向SRC写入NUM个字节并以'$'字符结尾
WRITE_TO_SRC:
PUSH PSW
PUSH Acc
MOV R0, #SRC ; 将SRC地址加载到R0寄存器
MOV R1, NUM ; 将NUM值加载到R1寄存器 ,为需要循环的次数 ,10次
MOV A, #01H ;数值为1开始写入
JZ WRITE_FINISH ; 如果NUM为0,则跳转到WRITE_FINISH
WRITE_LOOP:
MOV @R0, A ;将A的值存入SRC地址中
INC R0 ;地址加一
INC A ; 示例:每次循环增加A的值(可以替换为其他逻辑)
DJNZ R1, WRITE_LOOP ; 减1并判断R1(循环次数)是否为0,不为0则继续循环
WRITE_FINISH:
MOV A, #'$'
MOV @R0, A ; 将'$'字符存储在最后
POP Acc
POP PSW
RET
// 子程序:将SRC中的数据逆序复制到DEST并以'$'字符结尾
COPY_TO_DEST:
PUSH PSW
PUSH Acc
MOV R0, #SRC ; 将SRC地址加载到R0寄存器
MOV R1, NUM ; 将NUM值加载到R1寄存器
MOV A, NUM
ADD A, R0
ADDC A, #-1
MOV R0, A
MOV DPTR, #DEST ; 将DEST地址加载到DPTR(用于片外数据存储器访问)
COPY_LOOP:
MOV A, @R0
MOVX @DPTR, A ; 将A的内容写入到DEST地址
INC DPTR ; DEST地址加1
DEC R0 ; SRC地址减1(逆序)
DJNZ R1, COPY_LOOP ; 减1并判断R1是否为0,不为0则继续循环
; 写入'$'字符到DEST的末尾
COPY_FINISH:
MOV A, #'$'
MOVX @DPTR, A
POP Acc
POP PSW
RET
// 子程序:冒泡排序
ORDERS:
PUSH PSW ;先进行现场保护
PUSH Acc
MOV R0, NUM ;R0为外循环次数的初始值(原本应该是NUM-1,但是在上面程序中,其实我们多存入了“$”字符)
JB 7AH,BIG_TO_SMALL ;7AH为1则跳转(从大到小排列)
SMALL_TO_BIG:
OUTER_LOOP1:
MOV DPTR, #DEST
MOV A, R0
MOV R1, A ;R1为内循环的次数
INNER_LOOP1:
MOVX A, @DPTR
MOV R2, A
INC DPTR
MOVX A, @DPTR
MOV R3, A
SUBB A, R2
JC TO_SWAP1 ;CY=1(R3<R2),则需要转换
SJMP NO_SWAP1
TO_SWAP1:
MOV A, R2
MOVX @DPTR, A
DEC DPL
MOV A, R3
MOVX @DPTR, A
NO_SWAP1:
INC DPTR ;每次内循环结束,dptr值加一
DJNZ R1, INNER_LOOP1 ;每次内循环结束,内循环次数值减一,不为0则继续内层循环
DJNZ R0, OUTER_LOOP1 ;每次外循环结束,外循环次数值减一,不为0则继续外层循环
//排序结束,跳转到结束
SJMP ORDER_FINISH
//从大到小排序
BIG_TO_SMALL:
OUTER_LOOP2:
MOV DPTR, #DEST
MOV A, R0
MOV R1, A
INNER_LOOP2:
MOVX A, @DPTR
MOV R2, A
INC DPTR
MOVX A, @DPTR
MOV R3, A
SUBB A, R2
JNC TO_SWAP2 ;CY=0(R3>R2),则需要转换
SJMP NO_SWAP2
TO_SWAP2:
MOV A, R2
MOVX @DPTR, A
DEC DPL
MOV A, R3
MOVX @DPTR, A
NO_SWAP2:
INC DPTR
DJNZ R1, INNER_LOOP2
DJNZ R0, OUTER_LOOP2
ORDER_FINISH:
POP Acc
POP PSW
RET
END
五、总结和思考
1. 指针操作
由于 `DEC DPTR` 指令不存在,因此需要手动调整 `DPL` 来实现指针回退。例如:
DEC DPL ; DPTR = DPTR - 1
这种方法适用于不会发生借位的情况(即 `DPL` 不为 `00H` 时)。如果涉及跨页调整,则需要同时修改 `DPH` 和 `DPL`。
2. 寻址方式
-直接寻址(如 `MOV A, R0`):操作的是寄存器本身的值。
间接寻址(如 `MOVX A, @DPTR`):访问的是指针指向的内存数据。
立即寻址(如 `MOV A, #30H`):直接使用常数,不涉及内存或寄存器内容。
3. 排序逻辑优化
-通过标志位(如 `7AH`)控制排序方向(升序/降序),减少重复代码。
-内层循环次数随外层循环递减,避免不必要的比较。
4. 调试经验
在片外 RAM(`XDATA`)操作时,必须使用 `MOVX` 指令,否则数据无法正确读写。
排序前需确保数据范围正确,避免 `'$'` 等非数值字符干扰比较逻辑。
这些细节在实现排序算法时尤为重要,稍有不慎就会导致结果错误。后续可以尝试更高效的排序算法(如快速排序),并对比其性能差异。
更多推荐



所有评论(0)