【深圳 IO 攻略】第 9 关:无线游戏控制器

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

这一关我们需要在收到 rx 口的 -1 信号时,向 tx 口传送长度为 3 的数据包。
数据包的第一个数字为当前的 X 坐标;
数据包的第二个数字为当前的 Y 坐标;
数据包的第三个数字根据以下规则生成:

到这一关时,我有必要提一个之前一直被忽略的概念:p 口和 x 口的区别。我们的 MC 系列芯片有两种通讯端口,简单口(包括 p0、p1 两个口,简称 p 口)和扩展口(MC4000 有 x0、x1 两个口,MC6000 则还有额外的 x2、x3 两个口,简称 x 口)。p 口只能和外部的简单 I/O 端口或者另一块芯片的 p 口通讯,同样地,x 口只能和外部的复杂端口(带黄色三角形符号的端口)或者另一块芯片的 x 口通讯。只有 DX-300 这块芯片可以转换数据的通讯类型,它可以将至多 3 个只提供 0/100 信号的 p 口数据转换为一个三位数的 x 口数据,或者将一个从 x 口传来的三位数按照规则依次给最多三个 p 口设置 0/100 信号。如果要给 p 口设置其余的数字,那就只能通过 p 口设置,无法通过 x 口 + DX-300 这样的组合来设置。
说完了 p 口和 x 口的通讯方式,以及两种口如何相互转换外,我们来谈谈两种口在数据传输上有什么区别:
p 口传输的是电平信号,范围仅限 0~100。如果给 p 口传输超过上/下限的值,那么对应的 p 口只能接收到正好位于上/下限的值。x 口传输的是数字信号,能传输 -999~+999 范围内的数据。
p 口在一个时钟周期内只能传一个数,如果多次传输,则仅最后一次传输的值有效。x 口则可以传递任意长度的数据。
与第二条对应的是,p 口在同一个时钟周期内可以反复读取,而 x 口因为可以传递任意长度的数据,所以数据包里的每个数字都只能读一次。即使数据包里只有一个数字,也依然只能读一次,不能像 p 口那样反复读。
除 DX-300 及前两关里出现的 C2S-RF901 元件外,所有 x 口的数据采用同步协议传输。在同一个时钟周期里,必须要有一个 x 口发送数据,同时有另一个 x 口接收数据,且接收和发送的数据包长度一致时,这一次 x 口的通讯才算成功。如果在同一个时钟周期内,出现了一方发送另一方却不接收,或者一方接收另一方却不发送的情况,就会导致线程阻塞。
如图所示:






当然这一关的重点不在于“同步通讯”。这一关我们只需要知道 x 口如何在一秒钟内传输多个数就可以了。电路图和代码如下:

首先我们检测 rx 口是否大于 -999(tcp x0 -999)。如果满足条件,激活 + 号指令。但由于内部仍有更细致的判断,所以不满足条件时需要同时关闭 + - 前缀的指令,直接跳到最后休眠(slp 1)。如果这里的判断改用 tgt 的话,那么当读入 -999 时会错误地激活第 5 行指令,向 tx 发送本不该发送的数据包。tcp 指令相较于其他三种比较指令,多了一个“在特定条件下同时关闭 + - 前缀指令”的功能,可以让我们在书写逻辑嵌套代码时更加得心应手。这就是我为什么在遇到判断时喜欢优先用 tcp 指令的原因。
当我们读入的 rx 数据大于 -999(即为 -1)时,我们需要按照规则,依次发送 X 坐标(+ mov p1 x1)、Y 坐标(+ mov p0 x1),以及由 a b 的值计算出来的一个 0~3 范围内的值。因为一块芯片只有两个 p 口,所以 a, b 这两个口不得不使用 DX-300 转换成 x 口信号,接到 MC6000 的 x3 口上。由于 a 口和 DX-300 的 p0 相连,b 口和 DX-300 的 p1 相连,所以 a 口影响个位数,b 口影响十位数。a b 口的数据经过 DX-300 转换后的数字如下表所示:

我们可以发现,当 DX-300 为一位数时,output 和 DX-300 一致;当 DX-300 为两位数时,output = DX-300 - 8。我们下面的这段代码就完成了先计算出放在数据包里的值,然后发送给 tx,这样的任务。
点击左下角的【模拟】,稍等片刻,便会弹出结算界面:

优化代码行数
我们可以通过列数学公式的方式将 9 行代码减少到 8 行代码。现在观察这张表:

我们不难发现因变量 output 和自变量 a b 之间满足如下等式关系:

然后我们又知道神奇的 DX-300 可以把一个只提供 0 和 100 信号的 p 口信号缩小 10 倍(变成 0/10)甚至 100 倍(变成 0/1)输出出来。设两个口缩小 100 倍后的信号为 a' 和 b',那么我们现在只要给 a 口和 b 口各配一个 DX-300,把它们的信号缩小 100 倍后,计算 2b' + a' 的值,发送到 tx 即可。

这次的设计方案里,前三行代码和上一个方案完全一样,重点在于后面的代码。
由电路图可知,x2 得到的是 a',x3 得到的是 b'。那么
这三行代码就是用于计算 2b' + a' 的值的。计算完成后,将计算后的值发送给 tx(+ mov acc x1)就完成了一个数据包的发送。

我们通过列数学公式的办法,将代码行数减少到了 8 行。