【深圳 IO 攻略】第 29 关:变色鞋

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

本关需要在按钮按下时,根据传感器所表示的波长信息,将波长转换成颜色,并统计每种颜色的出现频率;并在按钮抬起时,找到出现频率最高的颜色,将它转换成对应的 SmartDye 颜色,再进一步转换成墨水 k 和墨水 n 信号输出。
每种颜色和光的波长,以及墨水 k、墨水 n 的映射关系参考数据手册:

本关是【深圳龙腾有限公司】系列里公认的最难关,其难度可以媲美【阿瓦隆城】的部分关卡。本关至少要三块 MC6000,还要同时配上一块 ROM 和一块 RAM 才能完成。这一关的接线图如下所示:

现在,我们像之前一样,把大问题分解成若干小问题来完成。
左边芯片的任务是:当按钮按下时,统计各种颜色的出现频率;当按钮抬起时,通知中间的芯片找到最频繁出现的颜色。
中间芯片的任务是:收到左边芯片的通知信号后,找到最频繁出现的颜色,并将对应的颜色编号告知右边的芯片。统计完毕后,将 RAM 中的统计信息清除。
右边芯片的任务是:收到中间芯片的通知信号后,把对应的最频繁颜色映射成 SmartDye 色彩空间的颜色,并进一步转换成墨水 k 和墨水 n 的信号输出。



首先我们来看最左边的芯片。根据数据手册里的说明,我们可以知道:颜色只跟传感器值的十位数相关,红 = 十位数是 2 或 3;橙 = 十位数是 4;黄 = 十位数是 5;绿 = 十位数是 6;蓝 = 十位数是 7;紫 = 十位数是 8。
首先我们检测按钮是否按下(tcp p1 50)。当按下时,读取传感器当前的值(+ mov p0 acc),并提取出十位(+ dgt 1),将该值作为 RAM 地址,改写对应位置的值。因为需要反复使用到这个地址,所以我们首先将该地址在 dat 里暂存一份(+ mov acc dat)。我们将 RAM 的地址设置为该地址(+ mov dat x3),提取出里面的频度数字(+ mov x2 acc),将频度 +1 后准备送回原处(+ add 1)。由于读取 RAM 后地址会自增,所以我们首先要将地址还原(+ mov dat x3),再将 +1 后的值送回原处(+ mov acc x2)。如此,便完成了当前这一秒的统计工作。跳到最后休眠一秒,进入下一个时钟周期(slp 1)。
如果按钮没按下时,检查 dat 是否为 0(- teq dat 0)。因为只要我们触发了统计过程,dat 的值一定会被更新为某一刻传感器值的十位数。所以当 dat 为 0 时,表示尚未触发统计过程,就不需要通知右边的芯片“找最大值”。只有当统计完毕,刚刚抬起按钮时,才需要通知右边的芯片去“找最大值”。这时候我们将 dat 归零(- mov 0 dat),回到“等待统计”的状态,然后给右边芯片发送一个 2(- mov 2 x1),通知右边的芯片去寻找最大值。至于这个 2 有什么用,我们后面再说。做完以上事情后,休眠一秒,进入下一个时钟周期(slp 1)。
有小伙伴可能会说,十位数不论是 2 还是 3 都是红色呀,但是你在以上的统计中只看了十位,十位是 2 以及十位是 3 的数字数量被分散在了两个不同的格子里,被视为了两种不同的颜色了呀?其实,这点我是有所考虑的。只是因为代码行数的限制,我无法在统计阶段就将两种红色合并,我们只能在后期“寻找最大值”的过程中将两种红色合并。

下面我们来看中间的芯片。首先我们等待右边芯片发来“给我找最大值”的信号(slx x3)。收到信号后,我们将 RAM 的右指针置为 2(mov x3 x1),然后连续读取两格 RAM,将它们的值相加,得到“红色”的出现次数(mov x0 acc, add x0)。我们先假设最频繁的颜色是红色,将“红色”的地址发送给右边的芯片(mov x1 x2)。由于 RAM 读取后地址会自增,所以实际发送的地址是 +1 后的地址。然后我们开始统计其他颜色的出现频数。我们将新颜色的频数放入 dat 中(mov x0 dat),检查新颜色的频数是否大于已找到的最大频数(tcp dat acc)。若大于,则说明新颜色才是迄今为止的最频繁颜色,我们覆盖掉原先较小的频数(+ mov dat acc),然后将新颜色的地址传给右边的芯片(+ mov x1 x2)。右边的芯片收到数据后,会改写墨水 k 和墨水 n 的值。而我们知道,同一秒内可以反复改写 p 口的值,最终只有最后一次改写的值会生效。所以,我们可以反复将找到的“迄今为止的最大值”传给右边的芯片,而不用担心是否会出现错误的信号。接下来,我们判定地址值是否到达了 9(teq x1 9)。尚未到达 9 时,跳回到第 6 行,继续判断别的颜色是否可能是更频繁的颜色(- jmp 6)。直到将所有颜色都统计完毕后,用循环的方式将整个 RAM 都写为 0,清除所有的颜色统计信息(mov 0 x0, teq x1 9, - jmp c)。

最后我们看右边的芯片。首先我们将墨水 k 和墨水 n 都初始化为 50(@ mov 50 p1, @ mov 50 p0)。收到中间芯片发来的地址值后(slx x2),我们将该地址值 ×2(mov x2 acc, add acc),然后到 ROM 中寻找相应颜色所对应的两个连续数字(mov acc x1),读取它们并依次发送到墨水 k 和墨水 n 端口(mov x0 p1, mov x0 p0)。由于中间芯片发送的是 +1 后的地址,因此红色对应的地址值是 4,橙色对应的地址值是 5,依此类推。我们将地址值 ×2 后,可以发现,8、9 两格对应的值是【血色】的 k、n 值(95,5),10、11 两格对应的值是【深玉米橙】的 k、n 值(50,5),依此类推。也就是我们将原始颜色的地址值 ×2 后,在 ROM 中的对应位置就可以匹配上相应的 SmartDye 颜色的 k、n 值。

点击左下角的【模拟】,稍等片刻,便会弹出结算界面:
