电玩圈游戏网 搜一搜

安卓手游去频道 >

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

安卓应用去频道 >

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

游戏视频去频道 >

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

资讯攻略去频道 >

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

专题合集去频道 >

游戏专题 应用专题

排行榜单去频道 >

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

首页>资讯>游戏攻略>深圳SHENZHENIO第16关幽灵娃娃攻略

深圳SHENZHENIO第16关幽灵娃娃攻略

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

深圳IO是一款硬核的编程游戏,有着严谨的游戏内容,那么一起来看看第16关的幽灵娃娃的攻略吧。

主界面

由于游戏过分硬核,先放个手册中的图看看冰山一角:

不过这也正对应了嵌入式开发中会遇到的海量数据手册,相当程度上还原了嵌入式日常开发的情景。

游戏中设计到部分的编程有些类似于汇编语言,这里上手还是有一些些难度的,大家请做好准备,继续直接扔核弹:

不用害怕,在游戏过程中,会逐步引导你学会使用新的指令,对于新的器件,也是随着主线的进行逐步开放的

在游戏中,你不仅可以完成指定的基本目标,还可以挑战全网玩家,看谁能达成最佳优化目标。

鱼和熊掌不可兼得,多数情况下想要达成更好的性能就要增加成本啦,不过这正是优秀的嵌入式开发人员的意义所在——比你好,还比你的便宜。

第 16 关:幽灵娃娃

关卡展示

这一关提供了一个沾满灰尘的随机数发生器。我们要做的是:当随机数发生器生成 1 时,令扬声器播放【邪恶地笑】音效;而当随机数发生器生成 2 时,令扬声器播放【令人毛骨悚然的尖叫】音效。

这两种音效的波形我们可以在数据手册中找到:

这些音效数据都由大量的数字组成,仅靠 MC 系列芯片里的 acc 和 dat 寄存器已经无法存储。这里我们必须要使用到元件面板里的扩展存储元件。

扩展存储元件有两种:一种是 100P-14,黄底,有 14 格额外的存储空间,数据可读可写,但初值只能是全 0,任何写操作都必须在运行时完成,不能在运行前写好初始数据。相当于现实世界里的随机存储器(RAM)。

另一种是 200P-14,黑底,同样有 14 格额外的存储空间。但是里面的数据必须在设计电路板时就确定好,运行时只能读,不能写。相当于现实世界里的只读存储器(ROM)。

这两个扩展存储元件有以下共同点:

1. 都带有两个指针。读取 a0/a1 口可以获得当前的左/右指针的位置,范围 0~13;而向 a0/a1 口写数据则可以更改左/右指针的位置。当写入的地址值在 0~13 范围之外时,会自动把地址值设置为传入的数取除以 14 的余数。例如,向 a0 口传 15 时,会将左指针置为 1;向 a1 口传 -1 时,会将右指针置为 13。

如图所示,当执行完 mov 15 x0 后,下方 ROM 的左指针指向了地址 1

如图所示,当执行完 mov -1 x1 后,下方 ROM 的右指针指向了地址 13

2. 读 d0 口会读取到左指针所指向的数字,同时左指针自动向后移动一格。读 d1 口会读取到右指针所指向的数字,同时右指针自动向后移动一格。指针自增的这个特性非常优秀,可以让我们在读连续数据时不需要反复操作地址口,只要连续读数据口就完事了。下面举一个连续从 ROM 中读取三个数,并将 acc 的百位、十位、个位依次置为这三个数的例子:

执行完第一行的 dst 2 x1 后,ROM 的右指针读到 3,将 acc 的百位置为 3。同时右指针向后移动一格,现在右指针位于地址 1 处。

执行完第二行的 dst 1 x1 后,ROM 的右指针读到 1,将 acc 的十位置为 1。同时右指针向后移动一格,现在右指针位于地址 2 处。

执行完第三行的 dst 0 x1 后,ROM 的右指针读到 4,将 acc 的个位置为 4。同时右指针向后移动一格,现在右指针位于地址 3 处。至此 acc 的三位数均已设置完毕,acc 的值为 314。

推理可得,执行完全部 8 行代码后,acc 的值为 159,ROM 的右指针指向了地址 6

3. 对于 RAM 而言,d0/d1 口不仅可读,而且可写。向 RAM 的 d0/d1 口写数据时,左/右指针指向的空间里的内容会被覆盖为新内容,同时左/右指针自动向后移动一格(指针自增这一点无论读写都一样)。下面给出一个将 ROM 中的内容复制到 RAM 中的示例程序:

首先我们从 ROM 的数据口(x0)读入数据,并将读入的数据写入 RAM 的数据口(x2)(mov x0 x2)。执行完本次操作后,ROM 的右指针以及 RAM 的左指针都会自增 1。此时我们判断任意一个地址值,如果指向了 0(teq x1 0,或 teq x3 0),说明前一次读/写操作是在 13 地址处进行的,那么就说明所有的数据都复制完毕了,直接结束程序(+ slp 999)。如果自增后的地址值没有指向 0,说明前一次读写并不是在 13 地址处进行的,那么就要跳转回去循环执行复制操作(- jmp 1),直到将 13 地址处的数据复制完成为止。

运行程序,我们发现 RAM 最终会被写入和 ROM 一模一样的数据:

前面我们花了很大的篇幅介绍 ROM 和 RAM 这两个扩展存储元件,现在我们回到题目。这道题因为要在特定的触发条件下播放两种可能的声音波形,且这两种波形数据的长度均为 13。所以我们肯定是需要两个 ROM 来存储两种声音的波形数据的。

这道题我们发现可以将一块 MC6000 物尽其用,4 个 x 口,正好接在两个 ROM 的地址口和数据口上;剩下两个 p 口,一个接随机数生成器的输入信号,一个接扬声器的输出信号。一点不浪费!我们先二话不说,搭出如下的电路图:

左边的 ROM 记录的是《邪恶地笑》的波形数据,而右边的 ROM 记录的是《令人毛骨悚然的尖叫》的波形数据。这两个 ROM 都以 50 结尾,是因为播放完对应音效后,需要将扬声器的波形值重设为 50,而不能一直停留在音效的最终波形值上。

我们现在的思路就是:当随机数发生器生成 1 时,我们在接下来的 14 个时钟周期里不断读左边 ROM 的数据口,并将相应的波形发送给扬声器;同理,当随机数发生器生成 2 时,我们在接下来的 14 个时钟周期里不断读右边 ROM 的数据口,并将相应的波形发送给扬声器。

根据一开始举的例子,我们已经知道了读完整个 ROM 的条件是:读取数据后的地址值为 0。因此我们写出如下的具有循环结构的代码:

首先,我们要给扬声器赋上 50 的初始声波(@ mov 50 p1)。接下来,我们判断当前时钟周期里的随机数是否为 1(teq p0 1)。如果是 1,我们不断从左边的 ROM 中读取数据发给扬声器(+ mov x0 p1, + slp 1),并判断读取后的地址值是否大于 0(+ tcp x1 0)。如果地址值大于 0,说明当前声波还没输出完毕,需要跳到第 3 行继续发送(+ jmp 3),直到地址值为 0 为止,关闭所有的 + - 号指令,跳到最后一行,休眠一秒,等待下一次信号(slp 1)。如果一开始的随机数不是 1,而是 2(- teq p0 2),则改为从右边的 ROM 循环读取(+ mov x2 p1, + slp 1, + tcp x3 0, + jmp 8)。等到读取完毕,右边的 ROM 的地址值为 0 时,跳到最后一行,休眠一秒,等待下一次信号(slp 1)。

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

评论 (0)

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

上拉或点击查看更多