啥是LC-3?


LC-3是一种简单(可能?)的十六位机器语言,它具有没什么卵用且可能使您挂科的作用。该语言自发明以来饱受欢迎,它受欢迎就体现在它受欢迎个锤子的欢迎,内网外网啥资料找不到它受欢迎它受了个蔡徐坤的欢迎(全恼)。

不知道为啥奥大的计组要我们学这种冷门汇编(可能是因为好上手?希望他们是这么想的罢…)…感觉相关的资源都很少,不会的地方也找不到人问,学起来太痛苦了。思前想后,这门汇编内容上其实真的不难,觉得要是后来人学起这个因为资源缺乏再遭罪实在不值得,故撰此文,希望能帮大家省去一些被知识的海洋倒灌的时间和精力。

注:本文假设您已经具有基本的计算机组成原理的知识基础,即理解基本的二进制运算、机器语言、寄存器、内存寻址的常识。
二注:本文主要是给小白看的,讲得可能特别啰嗦(明明就是因为我这人本来就比较啰嗦x




この分割线だ




LC-3的基本指令结构和语法


LC-3是储存在内存中的指令集合,其由十六位二进制数组成,按照内存地址的顺序运行,通过读写寄存器并将值存入内存的方式运作。LC-3假设有从R0到R7的8个可调用的寄存器。

所有LC-3指令都符合 前4位为操作码,后12位为操作数的结构。对于不同的操作码的操作,后12位的操作数的具体结构不同。

一行LC-3指令大概长这样:

操作码  操作数
0000   000000000000

如果想在这行执行不同的指令,就需要在这行储存不同的操作码。


比如,假如我们要在这一行LC-3机器码中,对寄存器R1中的数+1。

我们首先找到加法操作的语法:
加法操作的语法
ADD的操作码是0001。当LC-3计算机发现这行代码由0001打头时,就会把后面的部分当作ADD操作的操作数。
Dst指储存加法运算结果的寄存器。
Src1指储存运算加法的一个加数的寄存器。
Imm5指要加的值

因此,我们想要的指令就为:

0001 001 001 1 00001

其等价于

R1 = R1 + 1

0001告诉计算机我们进行的是加法运算(即格式为「Dst = Src1 + Imm5;」的运算),而不是别的。
001指计算的值要储存在R1寄存器中,只有被指定的寄存器才会发生改变。也就是说如果这里指定的是010(R2寄存器),那么经管你取出的是R1的值进行的运算,R1也不会发生改变。
就好比:

R2 = R1 + 1;

变的是R2,R1是不变的。(我是不是讲太细了x)

第二个001指取出R1中的值作为加数。
1指后面的加数为常量,如果这里为0,后面的加数就不是一个二进制常量,而会被当成一个寄存器了。
00001指加数常量。

加数有两种形式

Dst = Src1 + Imm5;

只是其中一种,还有一种为

Dst = Src1 + Src2;

机器语言的语法格式为
第二种加法运算
可以看见,二者的主要差别在于从右数的第五位数。如果计算机读到这行指令由0001开头,就会再读右数第五位数,如果为1则把最后五位数作为常数,如果为0则把最后三位数作为寄存器。

例:

R2 = R1 + R3;

可表述为LC-3指令:

0001 010 001 0 00011

看到这这行应该用不着多解释了罢~

LC-3的主要指令

LC-3有着丰富(个锤子 )且强大(个屁 )的指令集。
主要包括:
操作指令:ADD, AND, NOT
数据迁移指令:LD, LDI, ST, STI, STR, LDR, LEA
程序控制指令:BR, JMP, RTI, TRAP, JSR/JSRR

操作指令

ADD
ADD0
ADD1

AND

对Src1寄存器中的每一位与Src2或一个常数中对应的每一位进行二进制 和操作,并存入Dst寄存器。
AND0
AND1

NOT

对Src1寄存器中的每一位进行二进制非操作,并存入Dst寄存器。
NOT

数据迁移指令

LD

将内存中的内容存入Dst寄存器。PCoffset9指PC偏移量。
LD
PC就是现在的指令所在的内存地址,offset指偏移量,⑨指笨蛋 ,9指占用9位二进制数。
意思就是把PC + offset9那个位置的代码放到指定的Dst寄存器里头。

比如,假如我们现在的代码在的地址是3000(PC = 3000),

0010 010 000001111

这行代码就意味着 把地址为3015(3000 + 15)处的内容存进R2中。


LDI(花里胡哨的)

LDI比LD复杂一点。它要多套一个娃。
LDI
LD是直接找到PC+offset的内容,然后指把这个内容搬进Dst。LDI要多一步,它先找到PC+offset,然后把PC+offset的值作为地址,再寻一遍址,然后把这个地址的内容存进Dst。(我就感觉到绕)

比如,还是用前面的例子,我们把LD简单改成LDI。

1010 010 000001111

这行代码就意味着 把地址为3015(3000 + 15)处的内容作为地址 把这个地址的内容 存进R2中。
假如3015的内容是1500,那么R2的值就会变成地址为1500的储存器的值。


ST

ST是LD的逆操作,它将寄存器Src中的值存入PCoffset中的内存空间。
LD是把内存中的内容拿进寄存器,ST反过来,把寄存器的内容拿进内存。
ST

STI
同理,STI是LDI的逆操作,把寄存器的指取出来,放进PCoffset的地址的内容指定的地址里去。
STI

LDR(更加花里胡哨的)
LDR
Base其实就是一个寄存器。LDR要做的就是把base寄存器中的内容取出来作为地址,找到这个地址后,给它加上offset偏移值,然后把这个地址中的内容存进Dst寄存器。

比如,假如R2中的值为1919,地址为1926的内存空间中存的值为1145,

0110 001 010 000111

这串代码指的就是把Base中的值(1919)取出来,加上7(变成1926),然后我们以之为地址,找到1926,并把里面的值存入Dst(R1)。然后R1就变成了1145了。

STR

我觉得任何一只猫咪都能轻而易举地猜到这肯定是LDR是逆操作。
STR
前面的步骤不变,把最后一步反过来(内存中的值存进寄存器变成寄存器中的值存进内存)即可。


LEA(好!很直接!)

LEA没有任何寻址,它仅仅只是把PC+offset的地址的值存进寄存器Dst。非常得友好,非常得好理解。事鉴语句。
LEA

程序控制指令


BR

这个屑指令tlld我课件没看懂百度谷歌查了好长时间都没找到相关的起码折腾了我两三个小时气死我学tnd屑指令wdnmd
(冷静下来)BR指令是LC-3中最基础的跳转指令,作用类似于高级语言中的while、 for等循环语句(也可以当if用),但是理解起来不那么容易。
这里推荐大家可以移步哔哩哔哩大学参考一下这个湾湾做的视频:

https://www.bilibili.com/video/BV1uK411J7qK?p=16
属实救我性命

BR语句的基本格式如下:
BR
nzp类似于高级语言中的判断表达式(就是C、java里的while语句中圆括号里的东西),n是negative的意思,意味着判断上一个操作的寄存器的值是否小于零,z是zero的意思,意味着判断上一个操作的寄存器的值是否等于零,p是positive的意思,意味着判断上一个操作的寄存器的值是否大于零。

什么?你问我计算机怎么判断上一个操作过的寄存器的正负值?专门取出来判断一遍吗?我本来也纳闷这个,后来我才知道LC-3的计算机里头专门有三个bit存上一个寄存器的值(这个我查了老半天(怨))。

LC-3的计算机中每进行一次寄存器操作,LC-3就会记录这个寄存器的正负值,并存在那三个bit中。比如我把-1赋值给R1,LC-3中记录nzp的值的那三个bit就会变成100(n为1,指这个值是负数)(不可能出现110这样的,没有数既是负数又是0)。

而语句中的nzp则用于判断LC-3中的记录nzp的三个bit中的值是否符合语句中的nzp。
比如,语句中的nzp为010,这的含义就是「假如上一个被操作过的寄存器的值为0,我就把PC (现在的执行的指令的地址)设为PC+offset,否则继续按正常顺序执行指令」。

可以理解为while (Rx(上一个被操作的寄存器) == 0) {}这样的语句。

如果语句中的nzp为110,就可以理解为while (Rx(上一个被操作的寄存器) <= 0) {}这样的语句。

如果为111,则相当于必然跳转。

总结:BR语句的含义就是,如果上一个操作过的寄存器的值符合指令中的nzp的条件,则对现有的PC进行跳转,否则继续执行下一个指令。

一般BR在使用时有两种用法。
一个是循环的实现。
在BR前面放一个寄存器运算指令。比如R1 = R1 - 1;这样的。然后在一开始设定R1位某个特定正数值,把br语句的nzp设定为001(若R1>0)或者011(若R1>=0),则将PC跳转至之前的某一个位置(offset为负数),也就是说重新执行一遍前面的一段指令。然后R1不断减一,直到为零或者小于零。这就是while和for的汇编底层实现。

另一个是条件分支判断,即if的实现。
和之前不同的地方在于吗,如果要实现if,offset值一般设定为正数,即假如符合条件,就跳过一段指令(不执行),不符合就执行这段指令。

(如果还没看明白可以私聊我orz,我语音+画图可能理解更清晰一些orz)


JMP

觉得BR用起来麻烦?学着糟心?看着头大?来用JMP罢!你用不了上当用不了吃亏…
JMP
JMP语法很简单,啥条件判断妹有,说跳就跳(无条件跳转),想跳哪就跳哪(与BR使用偏移量跳转不同,它使用的是直接寻址)。
JMP会读取Base寄存器中的数值作为地址,然后直接把PC置为该地址,简单粗暴直接,事鉴语句(确信)。


RTI(这个好像叫RET,没搞懂到底叫啥)

比JMP更加简单粗暴,直接指定一个常数作为地址,然后把PC设为这个地址。
格式: 1100 + 十二位常数。


TRAP

t rap,是一种rap ,是trapvector的缩写,意为陷阱矢量。

TRAP

它会先把现在的PC + 1的值给R7,然后把trapvector8中的值作为PC。

我其实不是很明白有明白的可以来教我一下,…


JSR/JSRR

JSR
JSR看起来就是把PC + 1交给R7,然后PC加上常量offset偏移值。(不是很理解这是在干啥x)
JSRR
JSRR也很粗暴,也是把PC + 1交给R7,然后PC变为Base寄存器中的值。
我在想假如Base寄存器指定为R7会咋样()。(可能会直接执行下一行罢)






码了一上午了有点累,也权当给自己复习复习。伪代码和网络编程的内容咱自己也有一些不明白,所以放到后面写罢orz,到时候整点例题来给大家看看
祝大家学习顺利愉快w

Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐