欢迎光临散文网 会员登陆 & 注册

【深圳 IO 攻略】第 13 关:古钱币付款终端

2022-06-02 14:29 作者:ココアお姉ちゃん  | 我要投稿

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

关卡展示

自动售货机大家都见过吧,这关要实现的就是类似于自动售货机一样的东西。现在这台售货机里的商品价格已经定死了(写在【价格】常数芯片里),顾客投入 1/5/12 元的硬币,一旦任何时候累计价格大于等于商品价格时,就响 4 秒钟的铃,同时找零口开始给用户找钱,优先找 5 元硬币,找零剩余不足 5 元时开始找 1 元硬币。

由于找零的脉冲信号要维持 4 秒钟,且在此期间就要开始给顾客找零。所以为了方便,我们将收钱及响铃的程序写在一块芯片上,而找零的程序写在另一块芯片上。左边有三个投入口,我们肯定要把它们接到一个 DX-300 上,这样我们仅仅判断一个三位数就能得知哪个口投入了钱币。电路图如下所示:

首先我们写收钱部分,把相关的代码写在左侧芯片里:

从本关开始,我们要认识一条新的指令:跳转指令 jmp。它的用法是:jmp 行标,意思是无视原先的执行顺序,强制跳转到对应行标处执行。这条指令通常都是和 + - 号前缀同时使用的,表示“按条件跳转”。

这里我们之所以使用了 jmp 跳转指令,是因为和硬币投入口连接的 DX-300 其实是一个四态输入(没错,四种状态,连单纯的应付三种状态的 tcp 都无能为力):000 表示没有钱币投入;001 表示投入了 1 元硬币;010 表示投入了 5 元硬币;100 表示投入了 12 元硬币。如果少了最前面的(teq x1 0, + jmp 8)两条指令的话,那么后面的 tcp x0 10 在遇到 000 和 001 两种状态时都会激活同样的 - 号指令(投入 1 块钱)。这显然不是我们想要的。所以,当状态值为 000 时,我们需要使用跳转指令强制跳转到第 8 行的 slp 1,跳过 tcp 的三态判断。

下面我们来到了 tcp 三态判断。首先我们假设当前的状态值是中间状态 010(即用户投入的是 5 块钱),先令 acc +5 再说(add 5)。然后若 tcp 判定出 DX-300 的值位于两端状态,我们再做额外的调整(tcp x1 10)。当状态值是 001 时,说明客户投入的是 1 块钱,acc 要在之前的基础上 -4(- sub 4);当状态值是 100 时,说明客户投入的是 12 块钱,acc 要在之前的基础上 +7(+ add 7)。执行完上述操作后,我们得到了当前顾客已经累计投入的金额数量。

由于本游戏对于不等的判定只有【严格大于】和【严格小于】两种,所以对于【大于等于】这样的判定,我们只能用【不小于】来判定,即使用 tlt 测试指令,然后 - 号部分写“第一个数【不小于】第二个数时”的操作。我们先判断当前顾客已投入的金额数量是否【小于】价格(tlt acc x0)。若当前顾客已投入的金额【仍小于】价格,就休眠一秒,继续等待当前顾客投入更多的硬币(+ slp 1);若【不小于】价格,则将 acc 减去价格得到需要找零的数量(- sub x0),然后将找零值发给右边的芯片,委托右边的芯片执行找零任务(- mov acc x3),并响铃 4 秒(- gen p1 4 1)。做完这些事后,清除 acc 的值准备迎接下一个顾客(- mov 0 acc)。

左边的“收钱及响铃”程序已经完成,现在要开始完成右边的“找零”程序。在这里,我们需要学习 jmp 指令的另一个重要功能:实现循环。

假设你现在要往 p1 口发送一段共计 20 次脉冲的方波信号,你会怎么做?即使是代码空间最大的 MC6000,你也写不下 20 条 gen 指令。或者,脉冲次数不确定,由另一个输入口提供呢?你根本不知道要写几条 gen。

程序有三大结构:顺序结构、选择结构、循环结构。我们现在已经熟练掌握了前两大结构,现在我们就要学习如何利用跳转指令实现循环结构,生成共计 20 次脉冲的方波信号给 p1。以下是代码和解释:

如果把这样的汇编代码改写成 C 语言的样式,那大概长这样:

学过 C 语言编程的同学大概一眼就能看出来,while 语句块的部分就是一个非常典型的 20 次循环。

回到题目。当右边的 MC6000 芯片收到由左边芯片传来的“待找零数量”数据后,该如何找零呢?了解了程序的循环结构后,我们很容易设计出这样一套带有循环结构的算法:

  1. 将待找零数量放入 acc 寄存器。

  2. 判断 acc 寄存器的值是否大于 4。若满足条件,则令 acc -5,然后生成一个“找零 5 元”的脉冲信号,并反复执行此步,直到 acc 寄存器的值不大于 4 为止。

  3. 判断 acc 寄存器的值是否大于 0。若满足条件,则令 acc -1,然后生成一个“找零 1 元”的脉冲信号,并反复执行此步,直到 acc 寄存器的值归零为止。

  4. 执行到此处后,本次找零完毕,等待下一次找零信号的出现。

根据以上算法,我们在右边的芯片中写出这样的代码:

首先,因为找零信号不会在固定的时间出现,而是仅当用户投入了足额的硬币后才会从左边的芯片传过来,所以我们必须要用 slx 指令来等待信号出现后才能读取找零值。然后,第 2 行对应算法的第 1 步;第 3~6 行构成了“找零 5 元”循环,对应算法的第 2 步;第 7~10 行构成了“找零 1 元”循环,对应算法的第 3 步。以上任务执行完毕后,回到第 1 行 slx,对应算法的第四步“等待下一次找零信号”。

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

优化电量

将左边那块芯片中倒数第二行的 gen p1 4 1 改成 gen p1 4 3 即可。这是我尝试出来的允许的最长睡眠时间。电量可以由 318 减少到 294。

碎碎念

右边那块 MC6000 芯片接了两个 p 口和一个 x 口,寄存器也只用到了 acc。如果能省掉一行代码,那么就可以毫不费力地换成 MC4000。但是我苦思冥想,结果却无论如何都省不掉哪怕一行代码。于是这块 MC6000 芯片换不成 MC4000 了,仅仅只是多一行代码而已啊,真的好不甘心。不知道读者们有没有办法能省出来一行代码,有的话请留言告诉我。

【深圳 IO 攻略】第 13 关:古钱币付款终端的评论 (共 条)

分享到微博请遵守国家法律