【深圳 IO 攻略】第 30 关:航空鸡尾酒调酒器

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

本关的【小键盘】会不定期地给出一个 1~7 的按键输入,我们需要根据不同的输入数字调配出不同的饮料。这些饮料的配方请参考数据手册:

小键盘数字和饮料种类的对应关系如下:
子弹杯伏特加(伏特加 × 1.5 盎司)
柠檬糖(伏特加 × 3 盎司,柠檬配料 × 1 盎司)
大都会(伏特加 × 2 盎司,莱姆配料 × 1 盎司,蔓越莓配料 × 1 盎司)
寇德角(伏特加 × 2 盎司,蔓越莓配料 × 2 盎司)
伏特加马天尼(伏特加 × 3 盎司,干苦艾酒 × 1 盎司)
螺丝锥子(杜松子酒 × 3 盎司,莱姆配料 × 1 盎司)
杜松子马天尼(杜松子酒 × 3 盎司,干苦艾酒 × 1 盎司)
对每种原材料来说,其 100 信号的持续时间是跟需求量相关的(0.5 盎司/秒)。
这道题很明显是个【打表】题,每一个数字都对应着一个固定的“原材料波形时序图”。我们使用 DX-300 甚至可以控制三个 p 口的时序。这道题需要控制六种原材料,也就是六个 p 口的时序,所以需要使用两块 DX-300。我们先把两块 DX-300 接到电路图上。

上方的 DX-300 控制【伏特加】、【杜松子】和【柠檬】的时序,下方的 DX-300 控制【莱姆】、【蔓越莓】和【苦艾】的时序。我们先看每种饮料和上方的 DX-300 建立出的时序映射关系是怎样的:
100 × 3s(伏特加 1.5 盎司)
101 × 2s + 100 × 4s(伏特加 3 盎司,柠檬 1 盎司。伏特加和柠檬同时在上方出现,所以该时序由两部分组成。等同于【伏特加 + 柠檬】1 盎司,伏特加 2 盎司)
100 × 4s(伏特加 2 盎司)
100 × 4s(伏特加 2 盎司)
100 × 6s(伏特加 3 盎司)
10 × 6s(杜松子酒 3 盎司)
10 × 6s(杜松子酒 3 盎司)
再看和下方的 DX-300 建立出的时序映射关系是怎样的:
(无)
(无)
110 × 2s(莱姆配料和蔓越莓配料各 1 盎司)
10 × 4s(蔓越莓配料 2 盎司)
1 × 2s(干苦艾酒 1 盎司)
100 × 2s(莱姆配料 1 盎司)
1 × 2s(干苦艾酒 1 盎司)
下方的时序图简单点,都是单一时序,甚至还有两种饮料没有时序。每种饮料的时序和 DX-300 的值及持续睡眠时间两个值相关,所以为了将每种饮料的这两个数字都存储下来,我们需要将小键盘的值乘以 2,得到一个地址,将两个数字放置在以该地址起始的两个连续空间内。然后我们写出下面这样的代码:


我们观察一下这个 ROM,发现 6、7 地址写的是 3 号饮料的配料及持续时长,8、9 地址写的是 4 号饮料的配料及持续时长,依此类推。所有饮料的配料及持续时长都写在饮料编号 ×2 地址起始处的两个连续空间里。
那么我们首先等待小键盘的输入(slx x3),并将小键盘的值乘以 2,得到需要跳转到的 ROM 地址(mov x3 acc, add acc),然后将计算好的地址值发给上方的芯片,以便后续让上方的芯片生成上方三种原料的时序图(mov acc x3)。此时,因为第 1、2 种饮料是不需要生成下方的时序图的,也就是说仅当地址值大于 4 时才需要生成时序图。当地址值大于 4 时(tcp acc 4),我们将 ROM 定位到该地址(+ mov acc x1),然后连续读取原料和持续时长两个值,生成对应的时序图(+ mov x0 x2, + slp x0),完成后将信号清除(+ mov 0 x2)。
现在我们开始生成上方三种原料的时序图,代码如下:


这里我们接触到了一条新指令,也是 MC 系列芯片指令集中的最后一条指令:空操作指令 nop。它的作用是:当前机器周期内不执行任何实际操作。nop 指令和下面这些指令的效果是一样的,都是不造成任何状态变化的指令:
空操作指令,既占行数,又费电,最后还不产生任何效果。那么为什么我们需要这样一条空操作指令呢?答案是为了传输上的同步。我们注意到,上方的 x2 口既和小键盘相连,又和下方芯片的 x3 口相连。由于小键盘同样的数字只传一次,所以我们希望的是从 x2 口获得下方芯片传来的数据,而不是获得从小键盘传来的数字。因此,等待唤醒后(slx x2),我们需要执行一次空操作(nop),让小键盘的数字被下方的芯片捕获(上方芯片 nop 时,下方芯片同时执行 mov x3 acc),接下来我们从 x2 口获得的(mov x2 x1)就是由下方芯片传来的地址数据了。如果没有这条空操作指令,每次唤醒后,小键盘的数字会随机被上方/下方的芯片之一接收,导致非预期的运行结果。
我们注意到,第 2 种饮料是特殊的,它的时序由两部分组成:101 × 2s + 100 × 4s。但是我们的 ROM 对于每种饮料都只能存储单一的时序。所以当获得的地址值是 4 时(teq x1 4),我们需要在开头额外执行一个 101 × 2s 的时序(+ mov 101 x3, + slp 2)。剩下的部分,和下面的芯片类似,定位到 ROM 对应的地址,生成时序后(mov x0 x3, slp x0)清除信号(mov 0 x3)。
点击左下角的【模拟】,稍等片刻,便会弹出结算界面:
