【深圳 IO 攻略】第 1 关:安全摄像头

本文首发于 B 站《深圳 IO》文集(https://www.bilibili.com/read/readlist/rl569860)。原创不易,转载请注明出处。
关卡展示


这一关是新手入门关卡。在这一关里我们需要掌握两条基本指令:mov 指令和 slp 指令。
说明:指令中的 I 表示一个整数(integer),R 表示一个寄存器(register),P 表示一个端口(port)。这点以后不再重复说明。
mov 指令用法:mov I/R1/P1 R2/P2,将【整数/寄存器1中的数/从端口1获得的数】送入【寄存器2/端口2】中。
slp 指令用法:slp I/R/P,令 cpu 休眠一定的时钟周期,休眠的周期数等于【指定的整数/寄存器中的数/从端口获得的数】。
我们现在观察下方的波形图,不难发现,【活动状态】端口需要循环输出 6 秒的 0 信号和 6 秒的 100 信号。【网络】端口稍微复杂一点,循环节为:0×4s,100×2s,0×1s,100×1s。初始的线路板里,【活动状态】端口的代码已经为我们写好了,我们来分析一下:
所以,对于【网络】端口,我们已经找到了循环节,所以只需要照葫芦画瓢写就 OK 了。我们从右侧的元件面板上拖入一个【MC4000】元件,然后将它的 p0 口与【网络】端口连线,并写出如下图所示的代码:

点击左下角的【模拟】按钮,运行程序,稍等片刻,便会弹出过关画面。游戏会从成本、电量及代码行数三个维度评判你的设计方案的优秀程度。

优化电量
这里我们需要提一个隐藏特性:简单 I/O 口(就是只能和芯片的 p0/p1 相连接的端口)未赋值时拥有初始值 0。那么我们其实没有必要在第一条指令就写 mov 0 p0,而是先写 slp 指令,将 mov 0 p0 这样的指令放到循环的最后。这样的话,两块芯片在整个周期里可以各省掉一格给 p0 口清零的电量,共计可以省掉 2 格电量。

优化代码行数
这里要引入一条可以大幅减少代码行数的指令:gen 指令。
gen 指令用法:gen P I1/R1/P1 I2/R2/P2
这一条指令的耗电量是 4 个单位,等价于以下 4 条指令:
另外,还有一个新特性需要说明一下:在一行代码前加上 @ 注解,可以确保对应的代码只在最开始执行一次,而不会被循环执行。
因此,我们可以将以上代码大幅度减少为下面这样:

众所周知,gen 指令生成的是以 100 起始的方波(100→0→100→0→……),但是这道题要生成的是以 0 起始的方波(0→100→0→100→……),我们如何才能用 gen 指令简化呢?其实我们只需要把第一行的 slp 挪到最后一行,然后在第一行加上只执行一次的 @slp 指令,然后就能用 gen 简化了。如下所示:
等价于
等价于
控制【网络】端口的芯片同理。此时我们的代码行数缩减到了 5 行。
进一步优化代码行数
要生成以 0 起始的方波,在代码开头加入只执行一次的睡眠指令是一个办法,还有一个办法就是将生成的 100 起始的方波用【非门】反转一下。这样就不需要额外的睡眠指令了。我们现在从元件面板上拖入两个【LC70G04】元件,这个元件的作用是将输入端(大头端)的非 0 数据转为 0,或将输入端的 0 数据转为 100 传给输出端(小头端)。注意两端的方向不能接反。
接完了以后,我们将两块芯片中多余的 @slp 指令删除。注意第二块芯片的 gen 指令里的时间要改掉,第一个立即数写 0 信号的时长,第二个立即数写 100 信号的时长,和之前正好反过来。此时,因为加了两个非门元件,所以我们的成本提高到了 8 元,但代码行数减少到了 3 行。

进一步优化电量
在第三部分【优化代码行数】的基础上,将第一块芯片中的代码改为五条带 @ 前缀的指令,可以将电量极致优化到 57。如下图所示,@ slp 6,@ gen p0 6 6,@ gen p0 6 6,@ gen p0 6 6 后,只剩下了最后 6 秒的 100 信号,此时只需要一条 @ mov 100 p0 即可,无需执行休眠 6 秒的指令。
