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
下载 C 3 Simulator Windows Version 3.01 (385.0K)
这个主要是模拟器,windows直接运行
htps://highered.mheducation.com/sites/dl/free/0072467509/104652/LC301.exe
下载后,双击运行
进入到安装目录
Simulate.exe 模拟器
模拟器只能打开.obj目标文件
LC3Edit.exe 编译器
可以通过以下三种方式(二进制,16进制,汇编)写代码逻辑,输出目标文件等结构
用汇编来写还是相对来讲简单的,写完之后,点击3,汇编的箭头按钮,就会保存在一个文件里,输出以下文件,当然,报错的情况下,它会提示错误。
- 123.asm 它是汇编文件本身
- 123.bin 它是二进制文件
- 123.hex 它是十六进制的文件
- 123.lst 它是程序的列表文件
- 123.sym 它是创建汇编的符号表
- 123.obj 目标文件,需要Simulate.exe 模拟器打开
下载 C to LC-3 Compiler (369.0K)
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
可以看到命令还是挺全的
编译C语言文件
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
HALTGLOBAL_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, #-1ADD 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 18lc3_printf
ADD R6, R6, #-2
STR R7, R6, #0 ;return address
ADD R6, R6, #-1
STR R5, R6, #0
ADD R5, R6, #-1ADD R6, R6, #-1
STR R4, R6, #0ADD R5, R5, #4 ;cheating with the bp (no longer bp)
LDR R4, R5, #0 ;got addr of format stringPRINTF_LOOP ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;LDR R0, R4, #0ADD R0, R0, #0 ;End of string? (0x0000)
BRz PRINTF_DONEADD 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, #0PRINTF_CHAR
OUTADD R4, R4, #1
BRnzp PRINTF_LOOPPRINTF_CHECKSTR
;is it %s?
ADD R2, R0, #0
LD R7, PRINTF_S
ADD R2, R2, R7
BRnp PRINTF_CHECKDEC ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;%sADD R5, R5, #1
LDR R0, R5, #0
PUTSADD R4, R4, #1
BRnzp PRINTF_LOOPPRINTF_CHECKDEC
;is it %d?
ADD R2, R0, #0
LD R7, PRINTF_D
ADD R2, R2, R7
;BRnp PRINTF_ERROR
BRnp PRINTF_CHECKHEXAND R2, R2, #0
ADD R2, R2, #-10 ;going to divide by 10 by using sub loop
BRnzp PRINTF_NUMPRINTF_CHECKHEXADD R2, R0, #0
LD R7, PRINTF_X
ADD R2, R2, R7
BRnp PRINTF_CHECKOCTAND R2, R2, #0
ADD R2, R2, #-16 ;going to divide by 10 by using sub loop
BRnzp PRINTF_NUMPRINTF_CHECKOCTADD R2, R0, #0
LD R7, PRINTF_O
ADD R2, R2, R7
BRnp PRINTF_CHECKBINAND R2, R2, #0
ADD R2, R2, #-8 ;going to divide by 10 by using sub loop
BRnzp PRINTF_NUMPRINTF_CHECKBINADD R2, R0, #0
LD R7, PRINTF_B
ADD R2, R2, R7
BRnp PRINTF_ERRORAND R2, R2, #0
ADD R2, R2, #-2 ;going to divide by 10 by using sub loop
;BRnzp PRINTF_NUM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;%d
PRINTF_NUMLEA 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 loopADD R5, R5, #1 ;acquire the binary number
LDR R0, R5, #0ADD R0, R0, #0
BRzp PRINTF_DECPOS NOT R0, R0 ;make num positive for sub loop
ADD R0, R0, #1PRINTF_DECPOSAND R3, R3, #0
ADD R3, R3, #-1PRINTF_DIVLOOP
ADD R3, R3, #1 ;num/10
ADD R0, R0, R2 ;R0 = num % 10 - 10
BRzp PRINTF_DIVLOOPADD 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 digitLEA 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 digitADD R0, R3, #0 ;num/10BRnzp PRINTF_DECPOSPRINTF_LASTDIGIT;LD R1, PRINTF_ASCII
;ADD R1, R1, R0
;ADD R1, R1, #10
;STR R1, R7, #0LEA 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 digitLDR R0, R5, #0
ADD R0, R0, #0
BRzp PRINTF_DECSTRINGLD R0, PRINTF_MINUS ;num was negative
ADD R7, R7, #-1
STR R0, R7, #0 ;stored ascii value negative signPRINTF_DECSTRING ;print the calculated string
ADD R0, R7, #0
PUTSADD R4, R4, #1
BRnzp PRINTF_LOOPPRINTF_ERROR
PRINTF_DONELDR R4, R6, #0 ;restore R4
ADD R6, R6, #1LDR R5, R6, #0 ;restore bp
ADD R6, R6, #1LDR R7, R6, #0 ;restore ret addr
ADD R6, R6, #1RETGLOBAL_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 X3000AND 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;假设第一位为最大值,存入R0NOT 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名成绩,判断是否为ALD R2,SCORE2;将指针指向第一名成绩ADD R2,R2,#1;LD R6,EIGHTYFIVE;R6存储-85LOOP LDR R0,R2,#0;R0存储当前查询成绩ADD R4,R0,R6;检查当前成绩是否大于绝对分数85分,若大于R5=1,小于R5=0BRzp 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数据存入x4100LD 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存储-75AND R7,R7,#0;人数统计归零LOOP0 LDR R0,R2,#0;R0存储当前查询成绩ADD R4,R0,R6;检查当前成绩是否大于绝对分数75分,若大于R5=1,小于R5=0BRzp 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,条件不满足,默认为CBRn 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数据存入x4101HALT;
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