Lab4 Sort and Count
Purpose
完成"排序和计数"
有 16 个学生的分数。如果学生得分在85分或以上,并且进入前25%,他/她将获得A。 如果他/她没有得到A,但得分75或以上,并且处于前50%,他/她将获得B。 注意:每个分数都存储在从地址 x4000 开始的连续内存位置中。 每个分数都是介于 0 和 100 之间的整数。 每个人都得到不同的分数。
condition:
- 程序应按升序(从小到大)对分数进行排序,并将它们存储在从地址 x5000 开始的连续内存位置。
- 程序应计算有多少学生获得 A 并将数字存储在 x5100 中。
- 程序应计算有多少学生获得 B 并将数字存储在 x5101 中。
- R0-R7 在开头设置为零,程序应从 x3000 开始。
Principles
Key issues:
怎么计数A和B的个数
根据题意,A和B只来自于分数前8名,所以我们可以在对数组排序后统计前8名中大于等于75和大于等于85分的个数,并且显然大于等于75的个数一定>=大于等于85分的个数,因此就可以分为两种情况:
- 当大于等于85分的个数大于等于4个:A的个数为4,B的个数就是大于等于75分的个数减4
- 当大于等于85分的少于4个:可以分为两种情况:
- 大于等于75分的和大于等于85分的一样多,此时A的个数就是大于等于85分的个数,而B的个数为0
- 大于等于75分的比大于等于85分的更多,此时A的个数就是大于等于85分的个数,而B的个数为大于等于75分的个数减去大于等于85分的个数
怎么排序数组
联想到高级语言编程时用到的许多种排序方法,冒泡排序、选择排序等,其中冒泡排序最简单,所以选择实现冒泡排序,由于排序结果要存储在x5000开始的内存中,所以排序前可以先将数据从x4000存至x5000中,再进行排序
Procedure
AND R1, R1, #0 ; >=75分的人
AND R2, R2, #0 ; >=85分的人
AND R5, R5, #0;
ADD R5, R5, #8 ; counter
LD R3, NEWADDR
ADD R3, R3, #15 ; x500F
SCORE ADD R5, R5, #-1
BRn COMP;
LDR R6, R3, #0 ;取数x
ADD R3, R3, #-1;
LD R4, NOT_75 ; 取-75
ADD R7, R6, R4; x-75
BRzp JUGA ;x>=75
BR SCORE;
JUGA ADD R1, R1, #1; >=75的人+1
LD R4, NOT_85; 取-85
ADD R7,R6, R4; x-85
BRzp CNTA ;x>=85
BR SCORE ;
CNTA ADD R2, R2, #1; >=85的人+1
BR SCORE;
COMP ADD R3, R2, #-4; 前50%中85分>=4个
BRzp CONI ;
BR CONII ;85少于4个
CONI AND R2, R2, #0;
ADD R2, R2, #4; 4个A
ADD R1, R1, #-4; B的个数就是大于75的-4
BR OUTPUT;
CONII NOT R4, R2 ;
ADD R4, R4, #1;
ADD R3, R4, R1; >=75个数-85个数
BRz CONIII ; >=75的个数和85一样
AND R1, R1, #0;
ADD R1, R3, R1;
BR OUTPUT;
CONIII AND R1, R1, #0;
BR OUTPUT;
以上程序段实现了统计A和B的个数,只对从x500F开始往下的8个数统计,首先统计8个数中,大于等于85分的个数和大于等于75分的个数,得到这两个数后,并且大于等于75的个数必然比85的更多或者至少一样多,所以可以分成两种情况来分析,分别是>=85分的人数>=4个以及>=85分的人数<4个,这里第二种情况可以分成两种情况:>=75的个数和85一样以及>=75的个数比85的更多
SORT ST R7, SAVE
LD R3, OLDADDR ;
LD R4, NEWADDR ;
LD R1, NOT_15 ; -15
INIT ADD R7, R0, R1
BRp STAR
LDR R2, R3, #0;
STR R2, R4, #0
ADD R0, R0, #1
ADD R3, R3, #1
ADD R4, R4, #1
BRnzp INIT
STAR LD R0, IS_15
FORI ADD R0, R0, #-1 ; for i
BRn BREAK
NOT R3, R0
ADD R3, R3, #1
AND R2, R2, #0
AND R1, R1, #0
LD R4, NEWADDR
FORJ ADD R7, R1, R3
BRp JUDGE
ADD R1, R1, #1
LDR R5, R4, #0 ; j
ADD R4, R4, #1
LDR R6, R4, #0 ; j+1
NOT R6, R6
ADD R6, R6, #1
ADD R7, R5, R6
BRnz FORJ
LDR R6, R4, #0
STR R5, R4, #0
STR R6, R4, #-1
ADD R2, R2, #1
BRnzp FORJ
JUDGE ADD R2, R2, #0
BRz BREAK
BRnzp FORI
BREAK LD R7, SAVE
RET
如上是使用的排序的程序段,
将其作为一个函数,程序开始时使用JSR调用它将数据排序好之后再统计AB个数,需要注意的是LC3默认将R7寄存器用来存储程序返回地址,并且函数体会改变R7内容,因此我们要将R7的内容先Save,程序结束时再恢复,否则函数结束不能正确返回。
Result
Debug:
过程中遇到的主要问题就是:上述的函数返回的问题,最开始没有意识到RET的地址默认是存储在中R7的,所以导致程序不能正常运行
Test:
解决方案就是申请一块内存作为SAVE存储R7内容,在函数开头将R7内容存入SAVE,结束时再取出SAVE存入R7,这样函数结束时就能返回到正确位置了
SAVE .BLKW #1
ST R7, SAVE
LD R7, SAVE
Judge:
在本地 LC3Tool \text{LC3Tool} LC3Tool调试完毕后,将代码整理如下:用于网站在线 lc3 \text{lc3} lc3评测
.ORIG x3000
JSR SORT
AND R1, R1, #0 ; >=75分的人
AND R2, R2, #0 ; >=85分的人
AND R5, R5, #0;
ADD R5, R5, #8 ; counter
LD R3, NEWADDR
ADD R3, R3, #15 ; x500F
SCORE ADD R5, R5, #-1
BRn COMP;
LDR R6, R3, #0 ;取数x
ADD R3, R3, #-1;
LD R4, NOT_75 ; 取-75
ADD R7, R6, R4; x-75
BRzp JUGA ;x>=75
BR SCORE;
JUGA ADD R1, R1, #1; >=75的人+1
LD R4, NOT_85; 取-85
ADD R7,R6, R4; x-85
BRzp CNTA ;x>=85
BR SCORE ;
CNTA ADD R2, R2, #1; >=85的人+1
BR SCORE;
COMP ADD R3, R2, #-4; 前50%中85分>=4个
BRzp CONI ;
BR CONII ;85少于4个
CONI AND R2, R2, #0;
ADD R2, R2, #4; 4个A
ADD R1, R1, #-4; B的个数就是大于75的-4
BR OUTPUT;
CONII NOT R4, R2 ;
ADD R4, R4, #1;
ADD R3, R4, R1; >=75个数-85个数
BRz CONIII ; >=75的个数和85一样
AND R1, R1, #0;
ADD R1, R3, R1;
BR OUTPUT;
CONIII AND R1, R1, #0;
BR OUTPUT;
OUTPUT LD R3, RESULT
STR R2, R3, #0
STR R1, R3, #1
HALT
;冒泡排序
SORT ST R7, SAVE
LD R3, OLDADDR ;
LD R4, NEWADDR ;
LD R1, NOT_15 ; -15
INIT ADD R7, R0, R1
BRp STAR
LDR R2, R3, #0;
STR R2, R4, #0
ADD R0, R0, #1
ADD R3, R3, #1
ADD R4, R4, #1
BRnzp INIT
STAR LD R0, IS_15
FORI ADD R0, R0, #-1 ; for i
BRn BREAK
NOT R3, R0
ADD R3, R3, #1
AND R2, R2, #0
AND R1, R1, #0
LD R4, NEWADDR
FORJ ADD R7, R1, R3
BRp JUDGE
ADD R1, R1, #1
LDR R5, R4, #0 ; j
ADD R4, R4, #1
LDR R6, R4, #0 ; j+1
NOT R6, R6
ADD R6, R6, #1
ADD R7, R5, R6
BRnz FORJ
LDR R6, R4, #0
STR R5, R4, #0
STR R6, R4, #-1
ADD R2, R2, #1
BRnzp FORJ
JUDGE ADD R2, R2, #0
BRz BREAK
BRnzp FORI
BREAK LD R7, SAVE
RET
SAVE .BLKW #1
IS_15 .FILL x000F
NOT_15 .FILL xFFF1 ;-15
NOT_75 .FILL xFFB5 ; -75
NOT_85 .FILL xFFAB ; -85
OLDADDR .FILL x4000
NEWADDR .FILL x5000
RESULT .FILL x5100
.END
评测结果如下,正常运行,输出正确。