一、实验目的
- 分析和理解指定的需解决问题。
- 利用LC-3的汇编代码设计实现相关程序。
- 通过LC-3仿真器调试和运行相关程序并得到正确的结果。
二、实验内容
对学生的成绩使用数组进行排序。
背景:一位老师需要你帮忙决定学生的成绩,她想要根据学生分数在班上的排名和考试分数一起决定学生最后的成绩等级。
成绩等级的具体要求如下:
- 如果学生的分数排名在全班的前25%,且考试分数达到85分及以上,则学生可以获得A
- 如果学生不能拿A,但是分数排名在全班的前50%,且考试分数达到75及以上,则学生可以获得B
- 剩下的学生都是C
具体的情况:
- 一共有16名学生,每名学生只有一个成绩
- 用汇编语言给这个班级写一个成绩排序的程序。
- 你的程序必须给学生的分数排序,然后计算出获得A和B的学生人数。程序从x3000开始
程序的输入:
- 班上16个学生的没有排序的成绩;
- 每个分数是用16位无符号数表示的0~100的整数
- 分数存储在16个连续的内存位置-每一个位置一个分数;从x3200开始;
- 最后一个分数的存储位置为x320F
- 你可以假设所有的分数相互之间不相同(每个分数是唯一的)
程序的输出(你的程序必须有两个输出):
- 16位学生的分数排序。分数必须按降序排序在连续的内存位置-每个内存地址一个分数,从x4000开始存储;即x4000存储的分数最高
- 获得A和B的学生的人数。获得A的学生人数必须存储在x4100,获得B的人数必须存储在x4101
三、实验步骤与结果
1.实验的总体思路与实现
首先要在内存x3200处输入16个分数的原始数据,并通过一轮循环,将其拷贝到x4000处。
在新地址使用冒泡排序降序排序,得出排名。最后对x4000处的数组进行筛选,即可统计出学生中得A级和B级的人数。总体流程图如下:
以上是一个抽象的、概括性流程,具体的实现方法将在以下展开。每一步具体的流程图都会在下文中提供。
a)完成16个数据的拷贝
算法流程图如下:
容易看出循环采用do-loop结构。
其中需要注意的地方有很多。例如给R3赋值,由于立即数在[-16, 15]的范围限制,需要两次才能完成。又如要实现*R2 <- *R1,可使用LDR和STR两条指令完成。除此之外,我使用寄存器R0长期保存新地址x4000。汇编代码如图:
b)对16个数据进行冒泡排序
为了让过程更易于理解,首先我使用C++语言实现了该算法,如下图:
对应的算法流程图如下:
由于在C++语言中,我采用了for循环。那么将其转移为汇编语言的过程中,我采用了Jump-to-middle的结构。汇编代码具体如下图:
c)对16个数据进行筛选评级
同样为了理解方便,先用C++编程。由于无需统计得C的同学的人数,于是循环8轮即可中止。代码如下:
然后是流程图。
在汇编化的过程中,对if语句无法使用复杂的表达式,因此会更多地用到条件跳转,类似于C++的goto语句。循环的实现依旧采用jump-to-middle结构。
由于多次涉及到和85、75这样的数据进行比较,如果使用减法,就会频繁地对寄存器进行取反加一的操作,这无疑是对时空资源的浪费。为了解决这个问题,寄存器中仅储存-85和-75,这样比较的时候,可以直接相加(相当于做减法),此法更优。
2.汇编代码的装载
依然在LC3Edit中完成编辑并生成obj文件,将其装载如LC3模拟器中,如图将学生成绩的原始数据保存在x3200的位置,即可进行实验。
3.代码功能的测试和正确性的检验。
以下主要采用两个比较有代表性的样例。这两组样例中,无重复值且有边界值。
样例1:75 85 90 60 73 87 100 38 66 80 76 82 93 95 74 86
运行结果如下图:
可见数据已经拷贝至x4000处。排序功能正常。
4100处的值是4,x4101的值是4。说明有4位同学得了A,4位得了B。这与笔算的结果是符合的。
样例2:75 85 60 61 73 84 100 38 66 53 76 82 72 95 74 80
运行结果如下图:
可见在x4000处可以看到已经排好序的数据。
与此同时,在x4100和x4101处可见,有3人成绩为A,5人成绩为B。这个结果是符合客观实际的。
完整代码
;author: CAO-Wuhui
;date: 2021.5.17
;function: Sort students' grades
;
;
;
.ORIG x3000 ;起始地址
;
;完成对原始数据的拷贝
;
LD R1 DATA ;R1指向源地址
LD R2 RESULT ;R2指向目的地址
AND R0 R0 #0 ;R0长期指向目的地址
ADD R0 R0 R2
AND R3 R3 #0
ADD R3 R3 #1
ADD R3 R3 #15 ;R3 = 16,循环次数
;
;循环,do-loop结构,将输入数据拷贝到目的地址
;
INIT_LOOP LDR R4 R1 #0 ;R4 = *R1
STR R4 R2 #0 ;*R2 = R4
;
ADD R1 R1 #1
ADD R2 R2 #1
ADD R3 R3 #-1
BRp INIT_LOOP
;
;冒泡排序,双重循环均采用 jump-to-middle 结构
;
AND R1 R1 #0
ADD R1 R1 #1 ;i = 1
BRnzp MIDDLE1
LOOP1 AND R2 R2 #0 ;j = 0
BRnzp MIDDLE2 ;
LOOP2 ADD R3 R0 R2
ADD R4 R3 #1
LDR R5 R3 #0 ;R5 <- *R3
LDR R6 R4 #0 ;R6 <- *R4
NOT R7 R6
ADD R7 R7 #1
ADD R7 R7 R5
BRzp END2
;
;exchange arr[j] and arr[j + 1]
;
STR R5 R4 #0
STR R6 R3 #0
END2 ADD R2 R2 #1
MIDDLE2 ADD R3 R1 R2
ADD R3 R3 #-16
BRn LOOP2
ADD R1 R1 #1
MIDDLE1 ADD R3 R1 #-16
BRn LOOP1
;
;排序结束
;以下开始筛选等级
;循环依旧采用 jump-to-middle 结构
;
AND R1 R1 #0 ;i = 0
;
;初始化R6 = -85,R7 = -75
;
LD R6 A_GRADE ;
LD R7 B_GRADE ;
NOT R6 R6
ADD R6 R6 #1
NOT R7 R7
ADD R7 R7 #1
;
AND R2 R2 #0 ;cntA = 0,A的人数
AND R3 R3 #0 ;cntB = 0,B的人数
;
BRnzp MIDDLE3
LOOP3 ADD R4 R1 #-4
BRzp IS_B
;
ADD R4 R0 R1 ;R4 = arr[i]
LDR R4 R4 #0 ;
ADD R5 R4 R6 ;if
BRn IS_B
ADD R2 R2 #1 ;cntA++
BRnzp NEXT
;
IS_B ADD R4 R0 R1 ;R4 = arr[i]
LDR R4 R4 #0 ;
ADD R5 R4 R7 ;if
BRn NEXT
ADD R3 R3 #1 ;cntB++
;
NEXT ADD R1 R1 #1
MIDDLE3 ADD R4 R1 #-8
BRn LOOP3
;
;最后将结果保存到指定位置
;
LD R1 A_CNT
STR R2 R1 #0
LD R1 B_CNT
STR R3 R1 #0
;
HALT
;
DATA .FILL x3200
RESULT .FILL x4000
A_CNT .FILL x4100
B_CNT .FILL x4101
A_GRADE .FILL x0055
B_GRADE .FILL x004B
.END