LC-3(Little Computer 3) 是一门教学用的虚拟计算机模型,主要是为了方便学生了解简单化的计算机结构。
主要想学习《计算机系统概论》上的案例,基本都是通过LC-3 模拟器和LC-3编译器来的,所以,把安装的方式学习一下。
这两个软件偏向于在Unix中安装,我这边没有那个环境,所以,我会把编译器安装在Ubuntu中,模拟器采用windows版的安装到本地。
https://highered.mheducation.com/sites/0072467509/student_view0/lc-3_simulator_lab_manual.html
这个主要是模拟器,windows直接运行
htps://highered.mheducation.com/sites/dl/free/0072467509/104652/LC301.exe
下载后,双击运行
进入到安装目录
模拟器只能打开.obj目标文件
可以通过以下三种方式(二进制,16进制,汇编)写代码逻辑,输出目标文件等结构
用汇编来写还是相对来讲简单的,写完之后,点击3,汇编的箭头按钮,就会保存在一个文件里,输出以下文件,当然,报错的情况下,它会提示错误。
https://highered.mheducation.com/sites/dl/free/0072467509/104652/lcc.zip
解压后如下:
大概有这些东西
根据README描述,需要gcc 和 wish 和 flex ,我这边直接安装flex就会安装gcc了。
然后在当前目录执行命令
chmod +x configure
sudo apt install flex
./configure
然后,就会显示下边这个,就成功make了,然后,开始执行安装命令
make
make install
make 执行完
make install 执行完
这个时候就会在当前文件夹下,新增一个install的文件夹,其中 lcc就是编译器了
可以通过 lcc 直接执行来看看帮助信息
./lcc
可以看到命令还是挺全的
cd 到目标路径下
cd /root/lcc-1.3/install/
然后创建一个c语言的程序,如下
int main()
{
int a=1;
int b=2;
int c=a+b;
printf("data:%d",c);
return 0;
}
执行编译命令,就会生成a.asm文件,竟然有4KB大小
lcc main.c
最后可以看到很长的lc3的汇编(a.asm)
.Orig x3000
INIT_CODE
LEA R6, #-1
ADD R5, R6, #0
ADD R6, R6, R6
ADD R6, R6, R6
ADD R6, R6, R5
ADD R6, R6, #-1
ADD R5, R5, R5
ADD R5, R6, #0
LD R4, GLOBAL_DATA_POINTER
LD R7, GLOBAL_MAIN_POINTER
jsrr R7
HALT
GLOBAL_DATA_POINTER .FILL GLOBAL_DATA_START
GLOBAL_MAIN_POINTER .FILL main
;;;;;;;;;;;;;;;;;;;;;;;;;;;;main;;;;;;;;;;;;;;;;;;;;;;;;;;;;
main
ADD R6, R6, #-2
STR R7, R6, #0
ADD R6, R6, #-1
STR R5, R6, #0
ADD R5, R6, #-1
ADD R6, R6, #-3
ADD R7, R4, #12
ldr R7, R7, #0
str R7, R5, #0
ADD R7, R4, #11
ldr R7, R7, #0
str R7, R5, #-1
ldr R7, R5, #0
ldr R3, R5, #-1
add R7, R7, R3
str R7, R5, #-2
ldr R7, R5, #-2
ADD R6, R6, #-1
STR R7, R6, #0
ADD R7, R4, #3
ADD R6, R6, #-1
STR R7, R6, #0
ADD R0, R4, #1
LDR R0, R0, #0
jsrr R0
LDR R7, R6, #0
ADD R6, R6, #1
ADD R7, R4, #2
ldr R7, R7, #0
lc3_L1_main
STR R7, R5, #3
ADD R6, R5, #1
LDR R5, R6, #0
ADD R6, R6, #1
LDR R7, R6, #0
ADD R6, R6, #1
RET
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; void printf(const char *format, ...)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PRINTF_PERCENT .FILL -37
PRINTF_C .FILL -99
PRINTF_D .FILL -100
PRINTF_S .FILL -115
PRINTF_B .FILL -98
PRINTF_O .FILL -111
PRINTF_X .FILL -120
PRINTF_ASCII .FILL 48 ;postive ascii value of '0'
.FILL 49
.FILL 50
.FILL 51
.FILL 52
.FILL 53
.FILL 54
.FILL 55
.FILL 56
.FILL 57
.FILL 65 ;A
.FILL 66
.FILL 67
.FILL 68
.FILL 69
.FILL 70
PRINTF_MINUS .FILL 45
PRINTF_BUF .BLKW 18
lc3_printf
ADD R6, R6, #-2
STR R7, R6, #0 ;return address
ADD R6, R6, #-1
STR R5, R6, #0
ADD R5, R6, #-1
ADD R6, R6, #-1
STR R4, R6, #0
ADD R5, R5, #4 ;cheating with the bp (no longer bp)
LDR R4, R5, #0 ;got addr of format string
PRINTF_LOOP ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
LDR R0, R4, #0
ADD R0, R0, #0 ;End of string? (0x0000)
BRz PRINTF_DONE
ADD R2, R0, #0
LD R1, PRINTF_PERCENT
ADD R2, R2, R1
BRnp PRINTF_CHAR
ADD R4, R4, #1
LDR R0, R4, #0
;is it %c?
ADD R2, R0, #0
LD R3, PRINTF_C
ADD R2, R2, R3
BRnp PRINTF_CHECKSTR
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;%c
ADD R5, R5, #1
LDR R0, R5, #0
PRINTF_CHAR
OUT
ADD R4, R4, #1
BRnzp PRINTF_LOOP
PRINTF_CHECKSTR
;is it %s?
ADD R2, R0, #0
LD R7, PRINTF_S
ADD R2, R2, R7
BRnp PRINTF_CHECKDEC
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;%s
ADD R5, R5, #1
LDR R0, R5, #0
PUTS
ADD R4, R4, #1
BRnzp PRINTF_LOOP
PRINTF_CHECKDEC
;is it %d?
ADD R2, R0, #0
LD R7, PRINTF_D
ADD R2, R2, R7
;BRnp PRINTF_ERROR
BRnp PRINTF_CHECKHEX
AND R2, R2, #0
ADD R2, R2, #-10 ;going to divide by 10 by using sub loop
BRnzp PRINTF_NUM
PRINTF_CHECKHEX
ADD R2, R0, #0
LD R7, PRINTF_X
ADD R2, R2, R7
BRnp PRINTF_CHECKOCT
AND R2, R2, #0
ADD R2, R2, #-16 ;going to divide by 10 by using sub loop
BRnzp PRINTF_NUM
PRINTF_CHECKOCT
ADD R2, R0, #0
LD R7, PRINTF_O
ADD R2, R2, R7
BRnp PRINTF_CHECKBIN
AND R2, R2, #0
ADD R2, R2, #-8 ;going to divide by 10 by using sub loop
BRnzp PRINTF_NUM
PRINTF_CHECKBIN
ADD R2, R0, #0
LD R7, PRINTF_B
ADD R2, R2, R7
BRnp PRINTF_ERROR
AND R2, R2, #0
ADD R2, R2, #-2 ;going to divide by 10 by using sub loop
;BRnzp PRINTF_NUM
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;%d
PRINTF_NUM
LEA R7, PRINTF_BUF
ADD R7, R7, #15
ADD R7, R7, #1
;AND R2, R2, #0
;ADD R2, R2, #-10 ;going to divide by 10 by using sub loop
ADD R5, R5, #1 ;acquire the binary number
LDR R0, R5, #0
ADD R0, R0, #0
BRzp PRINTF_DECPOS
NOT R0, R0 ;make num positive for sub loop
ADD R0, R0, #1
PRINTF_DECPOS
AND R3, R3, #0
ADD R3, R3, #-1
PRINTF_DIVLOOP
ADD R3, R3, #1 ;num/10
ADD R0, R0, R2 ;R0 = num % 10 - 10
BRzp PRINTF_DIVLOOP
ADD R3, R3, #0
BRz PRINTF_LASTDIGIT
;LD R1, PRINTF_ASCII
;ADD R1, R1, R0
;NOT R2, R2
;ADD R1, R1, R2
;ADD R1, R1, #1
;NOT R2, R2
;;;;;ADD R1, R1, #10
;STR R1, R7, #0
;ADD R7, R7, #-1 ;stored ascii value of one digit
LEA R1, PRINTF_ASCII
ADD R1, R1, R0
NOT R2, R2
ADD R1, R1, R2
ADD R1, R1, #1
NOT R2, R2
LDR R1, R1, #0
STR R1, R7, #0
ADD R7, R7, #-1 ;stored ascii value of one digit
ADD R0, R3, #0 ;num/10
BRnzp PRINTF_DECPOS
PRINTF_LASTDIGIT
;LD R1, PRINTF_ASCII
;ADD R1, R1, R0
;ADD R1, R1, #10
;STR R1, R7, #0
LEA R1, PRINTF_ASCII
ADD R1, R1, R0
NOT R2, R2
ADD R1, R1, R2
ADD R1, R1, #1
NOT R2, R2
LDR R1, R1, #0
STR R1, R7, #0 ;stored ascii value of highest order digit
LDR R0, R5, #0
ADD R0, R0, #0
BRzp PRINTF_DECSTRING
LD R0, PRINTF_MINUS ;num was negative
ADD R7, R7, #-1
STR R0, R7, #0 ;stored ascii value negative sign
PRINTF_DECSTRING ;print the calculated string
ADD R0, R7, #0
PUTS
ADD R4, R4, #1
BRnzp PRINTF_LOOP
PRINTF_ERROR
PRINTF_DONE
LDR R4, R6, #0 ;restore R4
ADD R6, R6, #1
LDR R5, R6, #0 ;restore bp
ADD R6, R6, #1
LDR R7, R6, #0 ;restore ret addr
ADD R6, R6, #1
RET
GLOBAL_DATA_START
L1_main .FILL lc3_L1_main
printf .FILL lc3_printf
L5_main .FILL #0
L4_main .STRINGZ "data:%d"
L3_main .FILL #2
L2_main .FILL #1
.END
终于看到完整能输出lc3汇编的方式了。
//Cannot locate make binary. 那就是缺少 make 库
sudo apt install make -y
直接通过LC-3编辑器执行这个 a.asm 文件,会发现有这样的错误
目前查找的资料是说它未发现这些符号。
.ORIG X3000
AND R0,R0,#0;
AND R1,R1,#0;
AND R2,R2,#0;
AND R3,R3,#0;
AND R4,R4,#0;
AND R5,R5,#0;
AND R6,R6,#0;
AND R7,R7,#0;初始化寄存器
ADD R5,R5,#15;
ADD R5,R5,#1;外层循环16次
LD R2,SCORE2;指向存成绩空间
OUTLOOP ADD R6,R6,#15;内层循环15次
LD R3,SCORE;指针指向成绩
LDR R0,R3,#0;假设第一位为最大值,存入R0
NOT R0,R0;
ADD R0,R0,#1;R0最大值求补数
LOOP1 ADD R3,R3,#1;指针指向下一个数字
LDR R1,R3,#0;R1存入对比数字
ADD R4,R0,R1;R1>R0,将R0设为新的最大值R1,否则继续
BRp SWAP;将最大值设置为当前查询数
ADD R6,R6,#-1;
BRz GOOUT;内层循环结束,跳出二层循环
BRnzp LOOP1;
SWAP AND R0,R0,#0;
NOT R1,R1;
ADD R1,R1,#1;R1最大值求补数
ADD R0,R0,R1;
ADD R6,R6,#-1;
BRz GOOUT;内层循环结束,跳出二层循环
BRnzp LOOP1;
GOOUT LD R7,SCORE;遍历成绩表,与最大值相等的数据设置为-1。
LOOP2 LDR R1,R7,#0;
ADD R1,R1,R0;
BRz SET;
ADD R7,R7,#1;
ADD R1,R1,#0;
BRnp LOOP2;
SET LD R1,MINUS1;删除当前轮最大值(设置为-1)
STR R1,R7,#0;
NOT R0,R0;
ADD R0,R0,#1;R0为当前轮最大值
ADD R2,R2,#1;
STR R0,R2,#0;
ADD R7,R7,#1;
ADD R5,R5,#-1;
BRp OUTLOOP;
BRZ END;
END AND R0,R0,#0;
AND R1,R1,#0;
AND R2,R2,#0;
AND R3,R3,#0;
AND R4,R4,#0;
AND R5,R5,#0;
AND R6,R6,#0;
AND R7,R7,#0;初始化寄存器
ADD R1,R1,#4;遍历1-4名成绩,判断是否为A
LD R2,SCORE2;将指针指向第一名成绩
ADD R2,R2,#1;
LD R6,EIGHTYFIVE;R6存储-85
LOOP LDR R0,R2,#0;R0存储当前查询成绩
ADD R4,R0,R6;检查当前成绩是否大于绝对分数85分,若大于R5=1,小于R5=0
BRzp OVERABSOLUTE;
AND R5,R5,#0;
BRnzp NEXT2;
OVERABSOLUTE AND R5,R5,#0;
ADD R5,R5,#1;
NEXT2 ADD R5,R5,#-1;R5等于1,条件满足,为A,等于0,条件不满足,进行下一轮测试
BRn NO;
YES ADD R7,R7,#1;R7记录A等人数,加一
ADD R2,R2,#1;指针指向下一位成绩
ADD R1,R1,#-1;循环次数减一
BRp LOOP;
BRz END1;
NO ADD R2,R2,#1;指针指向下一位成绩
ADD R1,R1,#-1;循环次数减一
BRp LOOP;
BRz END1;
END1 STI R7,GRADEA;将R7数据存入x4100
LD R2,SCORE2;
ADD R2,R2,#1;
ADD R2,R2,R7;将指针指向第N+1名成绩
NOT R7,R7;
ADD R7,R7,#1;
ADD R7,R7,#8;
AND R1,R1,#0;
ADD R1,R1,R7;遍历N+1-8名成绩,判断是否为B,循环次数8-n存入R1
AND R6,R6,#0;
LD R6,SEVENTYFIVE;R6存储-75
AND R7,R7,#0;人数统计归零
LOOP0 LDR R0,R2,#0;R0存储当前查询成绩
ADD R4,R0,R6;检查当前成绩是否大于绝对分数75分,若大于R5=1,小于R5=0
BRzp OVERABSOLUTE1;
AND R5,R5,#0;
BRnzp NO1;
OVERABSOLUTE1 AND R5,R5,#0;
ADD R5,R5,#1;
NEXT4 ADD R5,R5,#-1;R5等于1,条件满足,为B,等于0,条件不满足,默认为C
BRn NO1;
YES1 ADD R7,R7,#1;R7记录A等人数,加一
ADD R2,R2,#1;指针指向下一位成绩
ADD R1,R1,#-1;循环次数减一
BRp LOOP0;
BRz END2;
NO1 ADD R2,R2,#1;指针指向下一位成绩
ADD R1,R1,#-1;循环次数减一
BRp LOOP0;
BRz END2;
END2 STI R7,GRADEB;将R7数据存入x4101
HALT;
SCORE .fill x3200;
SCORE2 .FILL X3FFF;
GRADEA .fill x4100;
GRADEB .fill x4101;
EIGHTYFIVE .fill #-85;
SEVENTYFIVE .fill #-75;
MINUS1 .FILL #-1;
.END;
可以先执行这个汇编和借用其中的方式方法。我大致看了,它没有使用直接编译的方式,而是直接手写汇编实现。
里面很多C语言里的函数都找不到标签,所以,相当于一些函数类的,无法搞定。对于学习也是够的了。
至此 LC-3 模拟器和编辑器以及编译器都安装完了,还挺麻烦的,找了很多资料,相对而言,还是有点老了。
参考了《深圳大学CS本科课程资源共享》,感谢大佬的作业。
https://github.com/Alex-Shen1121/SZU_Learning_Resource