comment
type
status
date
slug
summary
tags
category
icon
password
AI summary
一.基础逻辑电路
- 第一关:原力觉醒

第一关就是相当于基础介绍吧,意思是这个游戏我们可以通过点击左上角的输入切换状态来改变输出状态
- 第二关.与非门(NAND)

这关通过点击左上角的两个输入按钮,然后观察最后的输出再填到下面的”?”输出按钮的位置就行了
也可以根据我们小学所学到的与非门的特性,即“只有输入全为1时输出0,其余输出为1”直接填答案

- 非门(NOT) 观察与非门的第一列和最后一列,我们把非门的输入同时作为与非门的两个输入即可以利用与非门构建出非门

4-1 与门(AND)
与非门与与门的关系不就是差了一个“非”字吗?那我们直接把与非门再加上一个非门把与非门的输出全部反转就可以构建出一个与门了

4-2或门(OR)
或门是“两个输入同时为0时输出0,其余输出1”,再对比与非门的特性易知只需要把与非门两个输入反转就可以得到或门了

4-3 或非门(NOR)
类似于与非门和与门的关系,或非门与或门相互转换也只需要把输出反转一下就行了

- 高电平 这关要求构造一个始终输出高电平的电路,暂时不知道有啥用,随便搭一下就行了

- 第二刻 这一关让我们搭建一个电路使得只有输入1,0时输出为1,其余输出为0 这不禁让我们想到这个句子和与门描述都是”只有输入为xxx时,输出为1,其余输出为0”这种类型的,那便不难想到,我们只需要把与门的输入2取反,就可以得到该关所要求的电路了

- 异或门(XOR) 这关让我们搭建一个异或门电路,特性就是输入不同输出1,输入相同则输出0 看起来貌似有点棘手,但是回想上一关,我们让只有输入1,0的情况输出1,那么我们再弄一个只有输入0,1的情况输出1,然后把这两个用或门连接不就成了

我们可以发现,上述所有门电路貌似都可以通过与非门来构建,我们不妨尝试一下用与非门来构建异或门

- 三位或门与三路与门 直接用两个或门两个与门把三个输入连起来即可


- 同或门(XNOR) 对异或门输出取反即可

- 德摩根定律

至此,这个游戏第一章基础逻辑电路结束
二. 算术运算和存储器
- 成对的麻烦 这一关让我们构建一个电路,使得4个输入有两个及以上输入为1时输出为1 我们可以先判断其中三路是否至少有一个输入为1,再把剩下一路用与门连接起来,这样如果输出为1,则可以判断至少有两个及以上输入为1,但是并没用考虑全面,倘若两个输入为1的全在三路或门上呢?为了避免这个问题,我们可以让四路都分别接与门的一个输入口,然后其余三路用或门接入,或门输出接与门的第二个输入口,最后再用或门把四个与门的输出连接起来即可

还有一种办法就是让四路输入两两组合,然后用与门连接,这样只要出现两个及以上输入为1的情况必然有一个与门输出为1,再把所有与门输出用或门连接,就能完成了.这种解法看起来更容易让人想到也更简单

后面解锁注释功能和给导线分颜色了,我们把后面这个电路图整理一下方便观看和后面复制粘贴

- 奇数个信号 本关有四路输入,让我们在奇数个输入为1的时候输出1 回想如果是两个输入,用什么元器件可以快速判断奇数个信号呢?没错,当然就是异或门啦! 那么我们先判断各自判断两路是否有奇数个信号,倘若一个输出为0(0个或2个输入为1)一个输出为1(1个输入为1)那么就可以判断有奇数个输入为1了,这样我们再把两个异或门输出接另外一个异或门的输入就能完美解决问题了

- 循环依赖 循环依赖:个人理解就是元器件自身的输出直接或间接的接到自己的输入了

- 信号计数 本关要实现一个二进制计数器,有三个输出引脚,分别对应二进制的三位,输入为几(输入有几路为1)就把结果转换为二进制发送到输出引脚 最低位如果为1,那么输入就是奇数,否则为偶数,因此只需要用前面判断输入是否为奇数的电路即可(下图中3电路): 最高位也是非常简单,四路输入全为1,用与门连接即可(下图2电路): 中间那位就没那么好想了,我们简单回顾之前的关卡,有一关可以让两路及以上输入为1时输出为1,在本关两路及以上输入为1无非就2(010),3(011),4(100),除了输入为4时其余都会把第二位置1,那么我们把前面那一关的电路抄过来(下图图1),再让输入为4的时候把这一位置0即可(下图图4)

- 半加器 本关让我们构建一个半加器 当输入为0,0或者1,1时,相加结果转换为二进制(00,10)最低位都是0,输入为1,0或者0,1时,相加结果最低位均为1,不难发现这和异或门的特性是一致的,进位就更好搞了,直接用与门连接即可得到

- 延迟线 本关要求搭建一个延迟两刻的电路 两个延迟线连在一起即可

- 加倍 题目描述和解答如图:

- 全加器 本关要求制作一个全加器 不难发现进位就是判断是否有两个及以上输入为1,总和只存储最低位,如果最低位是0,则和为偶数,最低位是1,则和为奇数.我们就可以把与SUM和CAR连接的电路转换为与本章前两节相似的电路,本章第一节本章第二节.

- 奇变偶不变

- 1位开关 本关让我们用两个非门与两个开关来搭建一个异或门 注意到开关和与门的特性相似,且多个灰色的输出引脚可以连接到同一根输出线上而不引发冲突,你只需要确保任何时候最多只启用一个输出端即可,那我们即可参考之前搭建异或门的思路来搭建

- 1位取反器 此关输入输出对应的不就是异或门吗

- 8位或 对两个字节的数据按位或(一个字节等于八位) 把两个字节每一位分别进行逻辑或操作就行了

- 8位非 将字节数据按位取反 和上关思路一致

- 8位加法器 对两个字节进行相加 把两个字节每一位还有低位的进位相加即可,我们可以利用前面关卡解锁的全加器,输入就是两个字节对应的位以及低位的进位,输出就是求和结果和进位,依次连接起来即可

- 负数 简单介绍一下负数在计算机中的存储 在有符号整数中,一个字节有8位,其中最高位即为符号位,其余位都是数值位 符号位为1表示负数,符号位为0表示正数,比如说1000 0001就是表示-1,而0000 0001就表示1 那么计算机真的就是这么存储正数和负数的吗?如果把0000 0001和1000 0001相加,结果显然不是我们想要得到的结果0000 0000,所以计算机明显不是这么存储有符号整数的 如何让计算机简单实现1+(-1)==0呢?假设1在计算机储存的就是0000,0001,我们把它按位取反,就得到1111 1110,两者相加,就得到1111 1111,我们再将结果加1,就得到1 0000 0000,由于一个字节只有8位,所以最高位直接溢出了,不会存储在这个字节中,那么该字节存储的就是0000 0000,所以如果负数在计算机存储的是正数按位取反再加1的数,那么就可以逻辑自洽了,也更方便计算机做加减法 接下来就介绍原码反码补码的概念 计算机中存储的都是补码,前面提到的最高位符号位,其余位为数值位我们称为原码 正数原反补码都是相同的 负数的反码就是符号位不变,数值位按位取反,负数的补码就是反码+1 比如说-123,原码就是1111 1011,反码就是1000 0100,补码就是1000 0101 123的补码就是0111 1011,两者相加就是0了

- 数据选择器 如图,用两个开关控制输出哪一端即可

- 相反数 设计个电路,输出输入端的相反数 由本章15小节可知,按位取反再加一即可

- 总线 和前面数据选择器差不多

- 优雅存储 && 存储一字节 延迟线可以让输入值暂存一刻,但是如果能保存更长时间就好了,怎么办呢? 我们可以让延迟线的输出端接到它自己的输入端,这样就可以长久保存了,但是如果又要想修改存储的数据怎么办呢?有了!我们搞一个数据选择器,默认情况就让延迟线的输入端接它自己的输出端保证能长久保存,想要修改的时候就利用数据选择器让输入端接待写入的数值就行了

但数据选择器是八位的,延迟线却是一位的,所以要想存储一字节还得稍加改进

- 1位解码器 && 3位解码器 1位解码器直接上图言简意赅

一个输入,有2种结果(0或1),那么三个输入,就有=8种可能
我们可以利用穷举法把所有可能列出来,然后用与门连接起来,这样,就构成了一个3位解码器

- 逻辑引擎 创建一个可以对两路输入进行按位或(OR)、按位与非(NAND)、按位或非(NOR)以及按位与(AND)运算的设备。第三路输入是一个指令。指令是一个数字,它用于指示我们要进行什么样的运算 当前我们只有或门和非门,由第一章的德 摩根定律可知,对或门输入取反,就变成与非门了,对或门输入输出都取反,就变成与门,输出取反,就是或非门;指令又怎么搞呢?我们可以利用解码器加上分线器来控制四个开关,由此来通过指令指示输出的是什么样的运算结果

仔细观察本关的指令,还发现只要指令是奇数的(与非,与)就需要对输入取反,指令大于2(或非,与)就需要对输出取反,那么我们还可以用数据选择器来选择是否对输入输出取反

- 小盒子 设计一个电路,能随意向4个8位寄存器随意写入和读取信息,输入有写入读取的开关,还有两个地址位,可以组合4种不同的地址 首先利用两个地址位指定唯一的地址吧,这个简单,我们只需要用一位解码器,再用与门进行组合即可指定4个不同的地址 接下来就是写入和读取了,我们只需要把写入或读取的信号和对应的地址位用与门连接起来,再连接到8位寄存器对应的写入读取引脚,注意,读取的时候还得把输出端口给打开 因为空间有限,我们采用开关来替代与门这种方式来节省空间(效果好像差不多,都只有两路均为1的时候输出1)

- 计数器 搭建一个电路,每刻自动加1,还有擦写功能,开启时使输入的数值覆盖计数器的值 要实现每刻加1,那我们利用加法器即可,让存储的值和1相加,再写入寄存器 要实现擦写功能,我们只需用数据选择器来选择是否让寄存器写入输入的值,然后就轻松搞定了

算数运算和存储器这章结束
三.处理器架构
- 算数引擎 本关让在前面的基础添加加减运算的功能,要实现加减运算,我们用一个加法器就够了,然后利用数据选择器选择其中一路输入是否符号反转就可以利用一个加法器实现加减法了,最后在输出的地方加个数据选择器,就可以轻松解决

- 寄存器之间 搭建一个电路,使得能够把数据从一个地方复制到另外一个地方 游戏中关卡描述看起来很多,但其实思路很简单,要把数据从源复制到目的,只需要把所有寄存器输入输出连接在一条线上,然后打开源的读取开关和目的的写入开关即可,对于怎么指定对应的器件,利用分线器和解码器即可轻松解决

- 指令解码器 用分线器加一位解码器加与门即可

- 计算单元 是时候把你已经做好的“算数引擎”和“寄存器之间”电路合并起来了。在这一关里,你暂时只需要考虑复制模式和算术模式。点击工具栏中的“指令集“按钮来确认指令的格式。在算术模式下,请从1号寄存器(REG1)和2号寄存器(REG 2)中读取数值作为输入的操作数,并将结果保存到3号寄存器(REG3)中。 在”寄存器之间”这一节中,我们就是弄的复制模式的电路,要想只在复制模式启用它,只需要把指令解码器中复制模式的输出接口取反接到两个三位解码器的禁用端口即可. 接下来就是算数模式,我们先把计算引擎输入输出连接到对应的引脚,注意到计算引擎始终会输入输出,为了避免在其他模式的时候计算引擎也同时输出信号导致短路,我们用一个开关连接到计算引擎的输出引脚,保证其只有在计算模式下输出,最后记得在计算模式打开REG3的写入,由于REG3的写入引脚已经连接之前复制的输出引脚了,而正常情况下多个输出引脚是不能连接在同一个输入引脚,所有我们把原来的线和计算模式的输出用或门连接.这样就大功告成!

- 条件判断 如下图所示,根据3个条件位的组合(左边哪一列)确定判断类型,并根据判断类型检查数值,判断为真输出1

我们先判断value = 0的情况吧,如何判断呢?我们把数值连接分线器,把每一位分出来,然后把所有按位或,如果是输出是0,则value为0,但是我们想要value为0的时候输出1,那么把最后一个或门换成或非门即可,也可以不换后面添加一个非门,由于判断value=0时三位代码为001,那我们用分线器把条件位的每位分出来后用第一位与刚刚构建的电路用与门连接就好了(这里节省空间换成了开关,下面的那个也是如此)
接下来判断value < 0,根据前面章节学到的,有符号整数表示负数时最高位为1,由于判断value < 0代码为010,那么我们用与门与条件位第二位用与门连接起来即可
value ≤ 0,那不就是前两种情况或起来吗?我们把刚刚两种情况的输出用或门连接起来即可
Never,我们发现现在在三位代码为000的时候已经从不输出1了,不需要额外添加元器件了
其余几种情况,我们发现后面几种情况,三位代码除了最高位不同,其余位和前面的依次对应,且条件恰好相反,那么我们只需要在条件位最高位为1的时候,对刚刚的电路依次取反即可,我们可以利用非门加数据选择器实现,也可以把刚刚的电路输出和条件位最高位用异或门连接来达到相同效果

- 程序 程序左边连接一个计数器即可

- 立即数 本关要实现的功能就是把程序中的数字写到寄存器中去 要实现这个功能就比较简单了,我们把单字节的数据连接到REG0的写入端口,用开关使得只有在立即数的模式才打开,并且打开写入功能

- 图灵完备 我们最后要完成的一项任务,是添加一种指令并配置相应的硬件结构,从而实现条件跳转。 当指令的最高两位被置为1时,计算机应进入条件跳转模式。 在该模式下,我们将判断 3 号寄存器(REG3)中的数值是否满足由指令中最低的 3 位所指定的条件。 如果满足该条件,我们就用 0号寄存器中的值来覆盖程序计数器的数值。 如果能够有条件地改写程序计数器,就意味着我们能够在满足特定条件时,跳过某些指令,或让反复运行同一组指令。 我们前面关卡已经构建了判断条件的元器件,将它的条件输入端接在程序输出端,判断的数值接REG3输出端,因为只有跳转模式才需要判断,我们把判断判断结果后面加一个开关,用解码器的条件跳转接口控制开关,再接入计数器的擦写端口,然后REG0输出接入计数器的待写入的值的那端即可

本章完结
四.编程
- 加5等于几 读取输入,将输入加5,再输出 点击program右上角即可编程,所有的指令都是上一章构建的,如果忘了可以点击下方”查看指令定义”来查看指令集 因为之前的算法引擎是将REG1和REG2的内容运算输出到REG3中,所有我们要把5和输入分别存到REG1和REG2,然后相加,最后把REG3的内容输出.我们可以先利用前一章节的立即数模式把程序数字写进REG0,然后把REG0的数字再复制到REG1,之后把输入INPUT的数字复制到REG2,再将两者相加,最后将REG3的内容复制到OUTPUT即可

- 激光炮直瞄
本关目的就是编写程序来求周长,公式为2r,其中令3,r为输入
现在我们可以用汇编代码编写程序了,相当于为之前的代码取别名,使用别名来达到同样效果,更加人性化
由于我们算数引擎只有加法器,没有能直接计算乘法的东西,所以可能得利用加法器一个一个慢慢加了.
其中,加法运算肯定少不了算数模式中的加法以及像上一关那样复制内容到寄存器啥的.其中我们把加法的代码10 000 100(如果忘了可以点击左上方”查看指令集”来查看)取名叫add;
复制一个寄存器到另外一个寄存器可能就不是那么容易想出比较好的解法了,如果把每条指令全部取名未免有点太麻烦了,复制模式下的代码无非分为三部分,第一部分就是复制模式,第二第三部分就是源和目的寄存器,那么我们就把复制模式各部分分开,把10000000取名copy,把00 xxx 000取名rxxx(register寄存器)表示源的寄存器,例如00 001 000(r1)表示源的1号寄存器,把00 000 xxx取名to_rxxx表示目的的寄存器,输入输出也是类似的取名方法,最后根据我们小学学过的按位或知识就能将三部分组合起来,例如将REG3复制到OUTPUT,只需要
copy|r3|to_output
(相当于(10 000 000|00 011 000|00 000 110) = 10 011 110
)即可

取了这些别名,我们就可以用较为人性化的代码来编程解决问题了
输入为r,我们要求周长,因为,所以就是要求出6r,
那我们先用r+r得出2r,再用2r+r,得出3r,再3r+3r即可得出所要的结果

- 汇编常用技巧 再下一小节游戏开始之前,先看看手册里的汇编词条吧
- 注释:再代码中输入
#
,之后再该行后面即可注释,注释的内容不会被程序编译,只是方便编写代码和阅读的时候理解 - const修饰常量:前面我们知道可以用汇编代码来为代码取别名,让程序编写更加人性化,编写程序时还会遇到很多数字常量,我们可以使用const NAME num,来用NAME来替换num,这样和前面汇编代码效果类似,都是为了方便编写和阅读
- label:label可以让我们将该行的位置取别名,我们可以配合上一章最后一小节的条件跳转模式跳转到label的这一行,大概使用方法就是label某一行,然后想要跳转的时候先用别名将那一行的行号写入REG0,然后条件跳转模式判断REG3的数值是否符合跳转,符合就读取REG0的数值,跳转到那一行

- 太空入侵者 本关任务是给机器人编程,让它发射激光清除老鼠 因为上一发激光消失之前是不能发射第二发的,所以远程瞄准射击可能不太好解决,且机器人只能识别前面一格,所以我们可以等到面前再射击老鼠解决,下面贴出我的解决方法
效果如下:
- 密码锁
就是猜密码,当你输出正确的密码时,你就通过了这一关;输出任何错误数值都不会导致你输掉本关。另外,如果你的猜测值大于正确答案,输入将被设为1;否则输入为0。
解决方法也很简单,随便猜一个数,小了就加大了就减
- 时间掩码 本关要对4取模,也就是输出除以4的余数 任意一个数对四取模的结果都是小于等于3的,所以二进制下除了最低两位和输入相同,其余高位全部为0,要达到这样的效果,只需要对输入和0b011(十进制的3)按位与即可

- 迷宫 编写一段程序实现让机器人走出迷宫的算法 对于出口在边缘且没有回路的迷宫,我们可以始终左手(或右手)摸墙沿着走,就能到出口,我们可以画个图就知道这种方法能行了

至于究竟是个什么原理,你就想象两条平行线段夹成一条道路,一端是入口一端是出口,线段允许任意拉伸弯着,但是不允许交叉或剪断,现在把这两条线段盘曲折叠就成了迷宫的样子(摘自qq群群友),要走出去,只要沿着这条道路左边或者右边走即可
接下来就可以根据沿着左手摸墙的原则闭着眼来走迷宫了,对于人,就是先往前走,然后用左手摸一摸左边是不是有墙,如果没有墙就左转,之后从头开始重复,如果左边有墙,就右转,继续判断左边有没有墙,直到左边没有墙,最后摸到左边是门的就开门即可
对于机器人,无法判断左边的东西,所以写代码的时候要有所变化,具体思路就是先往前走,然后左转判断前面是否有墙,如果没有墙,之后从头开始重复,如果左边有墙,就右转,右转后继续判断前面有没有墙,直到前面没有墙.因为再判断前面是不是门太麻烦了,所以每次一开始就交互一下(踹一脚看看能不能踹开,能踹开肯定就是门了)来减少掉头发和代码量
效果如下:
本章结束
五.处理器架构2
- 异或
本关要求利用计算机中已经实现的指令功能,读取两次输入并输出两个值按位异或(XOR)的结果
我们之前设计的算数引擎只有加减,或,或非,与,与非的功能,要利用已经有的功能我们可以参考之前用与门非门或门搭建异或门的方法
不过好像不太简洁,网上查询发现另外一个用与门或门与非门搭建异或门的方法
我们发现与非门和或门真值表分别为:
ㅤ | NAND GATE | ㅤ |
input1 | input2 | output |
0 | 0 | 1 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 0 |
ㅤ | OR GATE | ㅤ |
input1 | input2 | output |
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 1 |
而异或门的真值表为
ㅤ | XOR GATE | ㅤ |
input1 | input2 | output |
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 0 |
那我们将两个输入分别按位与非和按位或,再将两个结果按位与不就行了吗
同样能实现
要求是要求,我们就不按照题目的要求来,直接将之前的计算单元再加个按位异或的功能试试



这样四行代码解决
- 8位常数 本关要求搭建个电路始终输出164

- 8位异或 搭建电路对每位异或 如果你仔细看了本章第一节,应该很轻松就能解决

- 相等 搭建一个电路,输入相等时输出1 前面同或门不就是这个玩意吗?不过前面的是一位输入,现在是八位输入,那么我们先搞个八位的同或门看看.不难发现,两个输入相同时,输出是-1,即一个字节八位都是1,输入不同时,一个字节八位就不为1,那么我们把输出再连分线器,然后把各位用与门连接起来再输出即可达到要求

- 无符号小于 搭建一个电路,第一路小于第二路(无符号)的时候输出1 注意到,任意一个数按位取反和原本的数相加二进制都为1111 1111,那么如果一个数按位取反和一个小于或等于自己的数相加,那么结果就小于等于1111 1111,如果大于自己的数就会溢出(产生进位).因此,我们可以利用判断是否溢出(产生进位)来解决这个问题 将第一路取反的输出和第二路输出接入加法器两个输入,然后将加法器进位输出即可

- 有符号小于 有符号小于就相比前一关多一个符号判断,那我们讨论一下有符号和无符号的区别. 倘若只有第一路最高位为1,那么无符号情况下第一路(大于等于128)是大于第二路(小于128)的,有符号第一路(小于0)小于第二路(大于等于0),有无符号判断结果相反;倘若只有第二路最高位为1,无符号情况下第二路大于第一路,有符号情况下第二路小于第一路,有无符号判断结果相反;倘若最高位均为0或者均为1,那么有无符号判断结果相同. 可以发现,如果最高位相同,那么有符号小于的判断结果和上一关相同,反之不同.那么我们只需要利用分线器和异或门来判断两个输入的最高位是否相同,据此再利用数据选择器来控制输出与无符号小于相同的结果还是相反的结果即可.

- 宽指令 设计电路,偶数时刻记录输出值,奇数时刻一次输入两个值 判断奇偶数只需要判断最低位是否为1即可,那么我们拿个信号计数器连接分线器,将最低位引出来即可:要偶数时刻记录值,那么我们只需要用个寄存器,在偶数时打开写入关闭读取奇数时刻关闭写入打开读取即可,第二路就简单了,用个开关让其只在奇数时刻输出即可

- 一把线,像挂面 前面第三章我们构建了一个叫OVERTRUE的计算机架构,我们也用来编了一些程序,缺点也很容易发现了,寄存器只能存储6位数,这往往会出现不够用的情况; 现在我们重新搞一个计算器架构,名为LEG架构; 首先我们按照关卡提示,添加程序元件,添加计数器,设置计数器步长,添加寄存器,链接监视


前面的计算单元只能计算REG1和REG2的运算,ALU始终是接的这两个输出端,但是现在我们需要能对多个寄存器任取两个运算,因此我们需要设置两个输出线,分别代表两个寄存器的输出值;对应的,我们希望寄存器能有两个输出端口,一个连接输出线一,一个连接输出线二,因此我们不妨进入元件工坊来改造一下我们的寄存器

大概就是这样,这样就可以通过下面两个接口来控制两个输出口的输出与否了(注意放置存储器探针,并打开监视状态与存储器相连);
设计成这样,照理来说我们就可以直接开始连线了,但是我们回想当时用解码器引出了那么多线,想想就麻烦啊!!!!那么我们能否想办法简化一下不连这么多线呢?我们知道,现在程序引出的四个输出中间两个就是指定寄存器地址的,我们要是能分别直接连接到寄存器控制输入输出的引脚,然后让寄存器自己根据地址来判断是不是轮到”我”上场了就好了.那到底能不能让寄存器这么聪明,直接根据地址判断是否该自己输出呢?呃呃,好像还真可以.
我们前面搭建过一个电路,让两个输入相等的时候输出1,还搭建过一个电路,能够始终输出一个常数,那么我们再添加一个输入,后续用这个输入来指定该寄存器的地址,然后利用八位判等器,一个连接刚刚添加的输入,一个连接控制输入输出的的引脚,那不就大功告成了吗?

最后画出来我们寄存器大概就是这样子
后面我们利用始终输出常数的元件给每个寄存器命名地址,当然,我们也需要对输入输出以及计数器如法炮制一下,原理差不多.别忘了更换了之前的寄存器重新链接一下监视
接下来就是连线了,程序输出的参数一二分别对应控制寄存器输出一二的接口,程序输出的操作码那位暂时不用管,本关只需要操作加法即可,输出一二我们就连接加法器,加法器的输出就分别连接输出端口,寄存器写入值的端口和计数器的擦写端口,程序输出的结果地址端口就连接输出启用端口,寄存器写入端口,和计数器切换步进擦写的端口即可

8月5号更新:
上图游玩后面的时候有个bug,就是系统输入始终开着,有时候执行其他命令会错过系统输入的值;解决方案也很简单,将两个开关有一个要打开的时候再开启输入即可,利用或门就能解决问题

- 操作码 本关要求如下图:

不难发现,这个指令和前面造的算数逻辑单元(ALU)指令不一样,所以我们重新搞一个
ALU
这个很轻松,相信凭借你自己不用看攻略也能行!
这里给出我的答案:

然后用新构建的ALU代替上一关的加法器,注意别把输入一和输入二连反了

- 立即数 先前我们的ALU都只能对寄存器的输出进行运算,本关我们需要实现直接从程序中读取数,也就是直接将程序的参数一和参数二作为数字进行运算(先前都是当地址来控制某个寄存器输出的),第8位为1时将参数1作为立即数,第7位为1时将参数2作为立即数; 这个就相当于让我们从4个输入数据选择其中两个,那不就是用数据选择器就行了吗,我们可以直接外面放两个数据选择器,然后利用分线器,将第8位和第7位接到数据选择器的控制端.但是最后看起来就有点小乱了.我们也可以选择将这个数据选择功能集成到之前设计的ALU上,但是本着低耦合高内聚的想法,我们还是给这个立即数功能单独设计模块吧!这样既美观还不至于让ALU模块过于臃肿

设计好我们直接接在ALU前面即可

轻松解决!
- 条件判断二 在你的电路中加入 if 语句跳转功能。在 if 语句中,你需要判断输入的 2 个参数是否满足要求,如果结果为真,就对计数器的数值进行擦写。
我们对该模块也进行一个封装,该元件有三个输入,分别为操作码,两个输入参数,有了前面解锁的符号判等器和8位小于比较器,我们可以轻松判断小于等于,再简单利用或门和非门,就能题目要求的几种判断条件,之后再用分线器解码器和开关来控制哪一个指令输出哪一个判断结果即可

接下来就将封装的元件接入之前的电路中,注意参数1参数2接在封装的立即数模块后面;因为条件跳转模式要对计数器数值进行擦写,所以我们利用或门将计数器中更改擦写/步进模式的接口把之前开启擦写模式的输入和现在开启擦写模式的输出或起来,计数器待写入值的接口利用数据选择器来控制写入哪个值

本章完结!
六.函数
- 移位 这一关的任务是,根据输入的第二个数,将输入的第一个数左移相应的位数.第二个输入不会超过 7.
想要移位,我们可以通过分线器集线器的错位来达到,那么不超过7的移位个数,就意味得搞7组分线器集线器吗?其实并不然,根据小学学过的加法知,7=4+2+1,那么我们只需要先左移1位再左移2位再左移4位即可

- 随机存储器 题目要求:向你的计算机中添加一个内存单元,使之增加 256 个字节的存储空间。你需要用一种技术手段来指定你访问的到底是内存中的哪个位置上的数据。更具体地讲,你需要额外添加一个寄存器,根据这个寄存器里的数值,来确定要访问的内存地址。每次读写内存前,你都需要先更新这个地址寄存器中的地址值。这一关里,你需要完成如下任务:从输入端依次读取 32 个数值,再按照输入的顺序将 32 个数原样输出。在读取完所有输入前就开始输出将被视作失败。 本关大概率是会用到编程的,点开左上角的指令集,发现这个LEG的指令集是空的,得我们自己添加,那么我们就先对LEG的各个指令命名

点击上图中这个加号就可以添加了,我们依次按照前面的指令添加名称

我们发现,后3位,第6,7,8都有用处了,第4位和第5位还没有用,那我们后面就用第5位来读取和写入应该能行吧
我们让第五位为1的时候进入读写ram模式,指定这个模式下最低位为0就是写,为1就是读

指令集这样的前置工作就完成了,那么我们就可以开始搭建硬件电路了
首先我们点右边的IO,内存,把这个内存的元件放出来

观察发现,左边有读取,写入,地址,待写入值几个接口,右边有输出的接口,根据前面的思路,第5位和第1位分别为1 0的时候写,第5位和第1位分别为0 1的时候读,那我们把操作码用分线器把各位分开,然后利用两个与门和一个非门即可实现这样的功能.如下图区域1所示

之后我们将ram的输出接到之前的输出总线上,为了避免数据冲突,我们在读写ram模式的时候用开关把ALU的输出给关闭了,如上图区域2所示;
然后就差地址和待写入的值了,我们就让程序中的参数1,2或者利用参数指定寄存器输出的值分别作为地址和待写入的值吧,那么我们就让立即数模块的的两个输出连上这两个接口即可
突然发现之前(8月5号前)设计的电路有点问题,就是每时刻关卡输入都会自动打开,导致错过很多输入,下面给出最初的解决方法(8月5号后已经更新前面电路,不会出问题,可以忽略这条消息)

只需要进入写入ram模式再打开系统输入就行了
接下来就是编写程序了,具体汇编别名我就不一一展示出来了,我取的别名很易读,写的注释也比较详细了,下面贴出我的代码:
8.20微调
为了后面关卡更方便操作,我们将电路简单调整一下,不难发现,我们之前画圈的地方,其实无异于一个1位解码器;

之前因为我们设计的指令集只需要判断第5位和第1位,因此无伤大雅,不过为了能方便后面添加功能,我们将这个1位解码器替换为3位解码器,其余电路不做改变

同样的,对指令集稍加修改:

- 延迟量 电路中的延迟量是由逻辑门的延迟量推导而来。基本逻辑门的延迟量都为2。串行连接逻辑门后,线路的延迟量会是这些逻辑门的延迟量之和。请设计一个电路,让它包含5个基本逻辑门,并具有6的延迟量。 一个基本逻辑门2延迟,那么串联三个基本逻辑门即可,剩下的只要不和这三个串联,就能满足要求

- 半字节乘法 要研究制作乘法器,那么我们就得先研究一下乘法是怎么算的 我们先拿十进制两位数相乘举例,比如12*13,实际上就是用12*3+12*10,而其中这个10,就是13中的那个”1”向左移一位得到的.现在换到二进制,比如5*6,二进制即为101*110,也就是101*0+101*10+101*100 = 101*0+1010*1+10100*1 = 1010+10100 = 11110 = 30(十进制),由上面第一个等号可知,我们把第一个数分别左移1,2,3,4位,然后再分别乘以第二个数对应位的那个数(也就是0或1),最后再相加即可;也就是按第二个数出现1的位数让第一个数左移相同位数,再将所有左移后的数相加即可 有了上面的结论,我们就可以设计电路图了,我们利用分线器将第一个数分别左移1,2,3,4位,再用第二个数对应位用开关来控制是否将左移后的数相加,最后将要相加的数全部加起来输出即可

- 除法 对输入的两个数值作除法,输出商和余数。例如,在除法7÷3中,7被3减2次后还剩下1。这里2 就是商,1就是余数。在这个练习中,你需要依次从输入端读取被除数(上例中为7)和除数(上例中为3),然后输出对应的商和余数。 由题目提示知,被除数=除数*被减的次数+余数,那么我们只需要用减法就能得出来了
- 栈 每一时钟刻,输入端都会接收到压栈 (PUSH) 或弹栈 (POP) 的信号之一(也可能两个信号都没有接收到)。如果收到压栈的信号,请将输入的数值存储在栈的顶部。而当收到弹栈的信号时,请移除栈顶数值,并将该值发送到输出端。该关卡的输出组件上有一个“启用吲脚,请仅在弹栈 (POP) 时向该引脚发送 0 来启用它。 我们先连接好输入输出端口,压栈的时候地址加1,所有我们先把计数器连接到地址那个接口,如下图所示;

压栈的情况考虑好了,那么接下来就讨论不压栈也不弹栈的情况,不压栈也不弹栈的时候地址肯定不变了,我们得让计数器不再加1,此时我们就让压栈关闭的时候打开计数器的擦写模式,然后将计数器的输出接入计数器待写入值的端口;

最后就是弹栈了,弹栈的时候我们就应该让指针(地址)移到上一个字节的开头,然后输出,也就是要将计数器的地址减1,那么我们就利用加法器,让原本的地址值-1,然后利用数据选择器,让弹栈的时候输出减1的地址,其他情况输出原来的输出即可(注意到弹栈的时候也会擦写计数器,且此时都不会压栈,所有控制计数器擦写的开关不用改);

- 压栈与弹栈 本关里,你需要为你的计算机添加一个栈,然后实现相应的指令,并自行编写一段程序以完成测试。当输入为 0 时,你需要弹出 (pop) 栈顶值,并将其发送到输出设备中。当输入不为 0 时,你需要将输入的值压入 (push) 栈中。 前面我们已经制作了一个栈装置了,接下来只需要放到我们搭建的LEG架构计算机即可; 我们可以将这个栈放在io模式下,通过特定指令来控制压栈和弹栈

不妨让io模式下的二进制10控制弹栈,二进制11控制压栈,值我们就让immediate模块输出的第一个值作为栈的值,输出连接前面的输出总线即可;
接下来就是添加指令集:

然后就是代码部分:
至于新添加的汇编别名,相信你能够独立完成!
- 函数 这一关里,你的任务是向你的电脑里添加负责函数调用和返回的指令: call 和 ret 。注意,当你从子函数返回调用点时,返回的位置应该在 call 指令的下一行,否则你的程序会进入死循环。函数调用指令 call 对应的具体操作如下:
- 将程序计数器的值和指令长度相加,并将其压入栈顶
- 令程序计数器跳转到函数入口处
- 弹出栈顶值,并将其写入程序计数器
函数返回指令 ret 对应的具体操作如下:
按照题目提示,我们得添加一个栈,在调用函数时,将当前位置(计数器的值)记录下来,使得调用完成后再擦写计数器,使其跳转到调用函数时的下一句语句.既然如此,那么我们每次调用函数时候都将计数器的值加8(压栈和调用函数我是分为两条指令的,每条指令4字节,因此要加8),再将结果压入栈中去,之后就可以直接调用了
电路部分和指令部分和上一关卡操作类似,就不具体描述了


这样操作之后,我们就可以每次跳转之前将地址压栈,返回时再弹栈到计数器,即可完成函数的调用和返回
本关需要自己判断是否实现对应功能,那就随便写代码即可,下面给出我自己检验的代码
效果如下:
本章结束!
六.汇编挑战
- ai打牌 游戏规则:桌面上共有 12 张牌,玩家和机器人轮流取牌,每次只能取走 1 ~ 3 张牌。玩家先取,取走最后一张牌的一方失败。输入端接收的数值为当前桌面上剩余的纸牌数量。向输出端发送数字 1 、 2 、 3 ,则玩家取走对应数量的纸牌。 NAK 02 会在玩家之后立刻采取行动,所以玩家可以在输出数值后立即读取输入,来获取机器人采取行动后的剩余牌数。
假设轮到我们抽的时候有2-4张牌,我们都能让最后一张牌让对面抽走,但如果轮到我们抽的时候有5张牌,因为我们最多只能抽3张,因此必输无疑(如果对面那个ai不是智障);因此,我们得让ai抽的时候还剩下5张牌.我们先取3张牌,然后对面取1-3张,那么就剩6-8张牌,恰好可以让对面取的时候剩下5张
- 机器人赛跑 让机器人沿赛道运动至终点请用尽可能短的程序来实现这一目标 本关的机器人看不到前面是啥,因此读取不了输入,所以前面编程那一章迷宫的方法就不能搬过来用,注意到本关只有一个地图,因此我们可以直接肉眼看图来判断它要怎么走,直接将要走的路线输出即可; 但是现在的LEG架构输出一个数就要4个字节,未免有点太麻烦了,我们点击右上角新建一个电路图,直接让程序输出的第一个接口接在关卡输出,然后计数器自增步长设置为1,这样我们就可以直接输出路线了

- 新品上市 传送带上每隔一段时间就会随机送入新的水果。用机器人监控传送带,一旦你看到同种水果出现两次,就立刻转身按下控制面板上的按钮。 思路就是先走到传送带前,然后读取输入判断是否有水果,第一次识别到该水果就将水果的编号作为地址写入1到ram中,之后再次识别到该水果就读取该地址的值是否为1,如果为1,那就说明该按按钮了.注意到我们每次都应先读取判断是否重复再选择按按钮还是写入才能保证正常运行
- 排序 本关要求读取一组输入,然后从小到大排序,依次输出 没啥好说的,就考我们排序算法,建议网上搜索思路再自己写 下面贴出本人写的垃圾代码,其中1是用的冒泡排序,写的确实垃圾,不建议参考,2是用的计数排序(应该?),空间占用比较大,可以稍微参考一下
垃圾代码1
垃圾代码2
- 跳舞机器 本关要求读取输入的值,然后根据题目中的步骤生成伪随机数 由于需要移位,我们要对之前的ALU进行升级

然后就是写代码了,注意到我们的ALU没有计算取模的功能,但是从前面的关卡易知我们只需要让数字按位与3得到的就是模4的结果了
- 汉诺塔 这个题目也是比较经典的算法题了,我一个电子专业不学算法的人都被人安利看过这个题 这关我们运用递归也是可以轻松拿下,想要了解具体原理的还是可以去网上搜一搜这个原理,不感兴趣的直接照着题目中给的伪代码抄即可
- 行星之名 这关让我们将一串字符串,每个首字母大写 我们发现,每次需要大写的字母(除了第一个)前面都是空格,那么我们每次判断一下刚刚输出的是否为空格,是就将下一个字母大写即可
- 水世界 这关对我来说过于复杂,然后寻求网友帮助,貌似还是没明白原理,下面贴出网友好心提供的C语言代码和我对照着写的垃圾汇编代码
C代码
汇编代码
通关!!
下图贴出我游戏中的大部分汇编代码





















