电玩圈游戏网 搜一搜

安卓手游去频道 >

角色扮演 射击枪战 棋牌卡牌 体育运动 角色扮演 赛车竞速 休闲益智 音乐舞蹈 模拟经营 战棋塔防 推理解谜 策略战争

安卓应用去频道 >

社交通讯 系统工具 影音视听 拍摄美化 效率办公 学习教育 生活服务 旅游出行 资讯阅读 金融理财 网络购物 游戏助手

游戏视频去频道 >

动作冒险 射击枪战 棋牌卡牌 体育运动 角色扮演 赛车竞速 音乐舞蹈 模拟经营 战棋塔防 推理解谜 策略战争 休闲益智

资讯攻略去频道 >

手游资讯 手游攻略 手游问答 游戏资讯 游戏杂谈 游戏攻略 软件教程 软件资讯

专题合集去频道 >

游戏专题 应用专题

排行榜单去频道 >

游戏排行 应用排行
首页 游戏 应用 视频 资讯 专题 榜单

首页>资讯>游戏攻略>深圳SHENZHENIO阿瓦隆城第2关精确计时器攻略

深圳SHENZHENIO阿瓦隆城第2关精确计时器攻略

作者:佚名来源:百度2022/06/29

深圳IO是一款硬核的编程游戏,有着严谨的游戏内容,那么一起来看看阿瓦隆城第2关精确计时器的攻略吧。

关卡展示

这一关看上去非常像【深圳龙腾有限公司】系列里的关卡。但实际做了以后才发现,这一关并不简单,要考虑的因素非常多,难度大致和龙腾第 29 关《变色鞋》相当。

本关要求实现一个精确计时器,这个计时器有【开始停止】和【关闭重置】两个按钮,均为多功能按钮。本关的要求如下:

1,初始状态下,计时器处于关机状态。

2,关机状态下按下【开始停止】按钮时,开机,并将时间置为 0。

3,开机状态下按下【开始停止】按钮,可来回切换计时/停止计时的状态。当处于计时状态时,每隔两个时钟周期,计时器的值 +1。

4,在开机且停止计时的状态下按下【关闭重置】按钮时,若计时器时间不为零,则先将计时器时间归零;若计时器时间已归零,则向计时器发送 -999 以关机。其余任何时候,按下【关闭重置】按钮都维持原状。

根据以上要求,我们可以将计时器细分为四种状态:①关机;②开机且正在计时;③开机且停在 0 时间;④开机且停在非 0 时间。那么,我们只需要用一个芯片来计算计时器的实时状态,另一个芯片根据实时状态来计算显示值,就可以完成任务了。电路图和代码如下:

由于【开始停止】和【关闭重置】两个输入量和下方的芯片直接相连,因此下方的芯片是真正意义上的“第一块芯片”。我们从这块芯片开始看起。

我们刚才说过,计时器一共可以细分为四种状态。我们将这四种状态分别编号 0~3。并存储到这块芯片的 acc 寄存器里。具体对应关系如下:

0:关机(且是初始状态)

1:开机且停在 0 值

2:开机且停在非 0 值

3:开机且处于计时状态

第 1~5 行代码的作用是检测是否有【关闭重置】信号,以及检测到信号时的操作。首先我们检查【关闭重置】信号是否出现(tcp p0 0),未出现时关闭一切 + - 号指令,跳到第 6 行。根据题目要求,处于计时状态时,不响应关闭重置信号。因此我们现在检测状态码是否小于 3(+ tcp acc 3)。状态码为 3 时,不执行任何操作,关闭 + - 号指令,跳到第 6 行。而当状态码小于 3 时,又细分成数值为 0 和非 0 两种情况。数值为非 0,状态码为 2 时(- teq acc 2),将数值清零但不关机,也就是将状态码变为 1(+ mov 1 acc)。数值为 0,或者处于关机状态时,状态码为 0、1 中的一个,此时需要将计时器关机,也就是将状态码变为 0(- mov 0 acc)。【关闭重置】按钮的逻辑就这么多。

第 6~12 行代码的作用是检测是否有【开始停止】信号,以及检测到信号时的操作。首先我们检查【开始停止】信号是否出现(tcp p1 0),未出现时关闭一切 + - 号指令,跳到第 13 行。【开始停止】按钮的逻辑较为复杂,因为不论当前处于什么状态,只要按下了【开始停止】按钮,状态都会改变。首先我们判断当前是否处于关机状态(teq dat 0),如果是关机状态,则开机,且令数字停在 0 值,也就是将状态码设为 1(+ mov 1 dat)。执行完后需要跳到第 13 行,避开第 12 行的 + 号指令(+ jmp d)。如果当前不处于关机状态,那么又要细分是否正在计时(- teq dat 3)。停在 0 还是非 0 不重要,那个是给【关闭重置】做逻辑判断的。如果不在计时,也就是状态码不为 3,则启动计时,将状态码改为 3(- mov 3 dat)。如果正在计时,也就是状态码为 3,则停止计时。停止计时后,时间肯定是处于非 0 状态,所以此时将状态码改为 2(+ mov 2 dat)。第 12 行的 + mov 2 dat 仅当第 10 行的测试指令 - teq dat 3 成立时才能执行,所以第 8 行的 + mov 1 dat 执行完毕后需要强制跳转下去,避免误执行这条 + 号指令。

操作完成后,将当前时钟周期的状态码发给左上角的“第二块芯片”,由它计算现在的计时器应该显示什么数字(mov acc x0)。最后,休眠一秒,进入下一个时钟周期(slp 1)。

现在开始看左上角的芯片。左上角芯片的 acc 寄存器用于记录计时器的实时数字(扩大十倍的数字),dat 寄存器用于接收下方芯片发来的状态码。我们首先将下方芯片发来的实时状态码存入 dat 寄存器(mov x1 dat),然后围绕着这个状态码做文章。当状态码是 0 时(teq dat 0),说明处于关机状态,将 acc 置为 -999 表示关机(+ mov -999 acc),然后跳到第 8 行,将 -999 发送给右上角的芯片(mov acc x3)。我们发现 7、8 两行把 acc 的值传了两次,这是因为右上角的芯片需要将扩大了 10 倍的时间做除以 10 的运算,在仅有两个寄存器的情况下,必须要两次提供原数才能正确地执行除以 10 的运算(这个我们后面再说)。而如果传的值是 -999 的话,我们不需要去做除以 10 的运算,这个值只需要传一遍就行了。所以当 acc 是 -999 时,跳过第一个 mov acc x3,直接执行第二个 mov acc x3。

继续判断,当状态码是 1 时(teq acc 1),我们需要将 acc 强制归零(+ mov 0 acc),至此,我们将 acc 的值传两遍给右边的芯片(mov acc x3, mov acc x3),并休眠一秒(slp 1)。

细心的读者可能会问了:状态码是 3 时你怎么不计时了?其实,我们不是不计时,而是【延迟计时】。状态码是 3 时,当前的时钟周期不能立刻做累加运算,而是要等到下一个时钟周期时才能开始累加。想象一下生活中的秒表是什么样子的:你刚按下秒表的时候,秒数还是 0,到了下一秒的时候,秒数才变为 1。这里我们也是一样的逻辑,休眠完毕后,检查上一秒的状态值是否是 3(teq dat 3)。若是 3 的话,计时器 +0.5(+ add 5)。

最后我们来看右上角的芯片。由于我们的计时器每 2 秒才能计一个数,所以实际上相当于每秒钟只能计 0.5 个数。而我们的游戏又不能使用小数,所以很无奈,上一个芯片只能把时钟扩大 10 倍,每秒钟计 5 个数,再交由这块芯片重新做一次除以 10 并向下取整的运算,得到实际显示的时间值。

我们之前说过,百位为 0 时,我们可以直接用 dgt 1 取十位来得到除以 10 的结果。而这道题的计时器是完全可以计到 10 以上的,也就是百位不一定为 0。这时候除以 10 就比较麻烦了。

首先我们得到从左边芯片传来的 ×10 后的数值,并放入 acc(mov x1 acc)。当然,因为 -999 只会传一次,所以我们首先检查原数是否是 -999(tcp acc -999)。如果是 -999,就别做什么除以 10 的操作了,直接跳到第 9 行关机就完事。如果不是 -999 的话,我们首先将这个数字的十位提取出来(+ dgt 1),并放到 dat 里备用(+ mov acc dat)。这时候,再从左边的芯片获得一份原数的副本(+ mov x1 acc),提取出百位(+ dgt 2),乘以 10,以便将这个放在最低位上的【百位数字】向前移动一位(+ mul 10),原先的最低位改成放之前存在 dat 里的【十位数字】(+ add dat)。如此,便得到了原数字除以 10 并向下取整的数。将这个数发送给显示器后(mov acc x3),休眠一秒,进入下一个时钟周期(slp 1)。

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

优化三项指标

我们的这个初版代码,主要的功耗都浪费在了【除以 10 并向下取整】这个过程里。其实,我们可以改为使用一个 ROM,里面放上 0、1 相间的数字,表示当前时钟周期里需要累加的值。这样相当于“前一秒计 0 个数,后一秒计 1 个数”,而不是之前的“每秒钟计 0.5 个数”。这样就成功避开了【除以 10 并向下取整】这样繁琐的运算。电路图和代码如下:

这个方案里,原先的三块芯片变成了两块芯片 + 一块 ROM,下方芯片也改为使用 x3 口来和上方芯片通讯。所以下方芯片的第 13 行代码由原先的 mov acc x0 改成了 mov acc x3,而下方芯片其余代码和上一版方案完全一致。

然后我们重点关注一下上方芯片。上方芯片此时的 acc 寄存器记录的已经是真实的显示器值了,而不是扩大十倍的值。所以其 x3 口已经直接和显示器相连了。与此同时,它的 x0 和 x1 口连接着一个 ROM 的数据口和地址口。

上方芯片的代码逻辑和上一版方案大同小异,简单过一下:首先从下方芯片获得实时状态并存入 dat(mov x2 dat)。状态值是 0 时关闭显示器(teq dat 0, + mov -999 acc),状态值是 1 时将计时值清零(teq dat 1, + mov 0 x1, + mov 0 acc)。这里注意,ROM 的地址口是和【计时状态下已经过的秒数】一致的,所以清零计时值不只是将显示的数值清零,经过的秒数也要清零。我们将当前秒数发给显示器并休眠一秒后(mov acc x3, slp 1)【延迟判定】上一秒是否处于计时状态(teq dat 3)。若上一秒处于计时状态,读一下 ROM 的数据口,令实际数字加上这个增量值(+ add x0,上一秒是偶数秒时 +0,上一秒是奇数秒时 +1)。

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

成本 ¥15→¥12,电量 1.4K→719,代码行数 35→24,三项指标都有质的飞跃。

评论 (0)

相关阅读
网友评论0条评论

上拉或点击查看更多