怎样知道FC等老式街机等老游戏机在运行时,CPU和内存的占用率?

CPU的占用率,简单来说,不需要知道

8bit时代,无论是6502还是Z80,指令就那么多,很容易计算子程序整体的Cycle,逻辑处理在帧渲染中处理,渲染(也就是设定VDP寄存器)在VBlank里面进行,NTSC环境下这些数字都是定死的。

那个时代,其中一些数字,比如一个水平刷新63.5微秒,一个垂直刷新16683微秒那都是要牢牢记在每一个程序员心中……

用FC举例:

一个HBlank有24个Cycle,考虑到RTI指令需要用掉6个,实际可用18个,你可以用超过这个数字的Cycle,毕竟中断这个东西一旦跳进了中断Handler,什么时候出来你说了算,但是如果你没能在左侧第一个像素开始渲染前设定好寄存器,画面左侧必然会出现一些奇怪的扭曲之类的

一个VBlank就是2507个Cycle,照样,减掉6个Cycle的RTI,2501

如果你不用光栅特效就可以无视HBlank,整体逻辑在29856-2507=27349个Cycle之内完成就好

如果用,HBlank中断Handler写好了之后算一下需要的Cycle,乘上240,减去它就是留给游戏逻辑可用的Cycle数,比如我用满24个,确保第25Cycle一定会RTI结束,那留给游戏逻辑就有21589个Cycle

什么?你的游戏太复杂?两万七千个Cycle之内实在完不成?

那就按30fps做呗,又不是没有这样的游戏

FC上有57175个Cycle(29856+27349-大概30Cycle足够判断奇偶帧了)足够你实现在时代限制下的基本所有想法了……

也有一些程序员脑洞清奇,比如岩田聪,F1 RACE里面奇数帧处理一部分逻辑,偶数帧处理另一部分逻辑……

那个年代开发游戏这些都要算的,不过那个时代也没有缓存,每一个指令,每一个内存访问需要的Cycle数完全固定,并不会有任何浮动,所以这些计算还相对比较容易

16bit时代,MD时钟周期有7.67MHz,街机用上68k更是12MHz起跳,基本上不需要算时钟周期,CPU机能实在太充裕了,一部分MD游戏和68k的街机游戏甚至充裕到CPU软件计算图像旋转,暂存到RAM里,然后用VDP渲染……

唯一需要算这个的是SFC,它时钟周期只有3.58MHz,FC的两倍,能用的Cycle是上面的数字乘2……当然作为16bitCPU,完成同样的工作所需要的Cycle数降低了,而且得力于强力的VDP,旋转缩放等等等等一应俱全,除了少数特效之外没有用光栅特效的需求了,但是整体上依然是机能有限……

内存占用率是需要知道的

但是那个年代并没有动态分配内存的需求,所有的变量用全局变量的思考方法去写程序就好

比如我做一个射击游戏,我一定会定一个上限,我的游戏同屏弹数不会超过100发,不会同时超过50个敌人之类的,然后在内存里依照这个分配好就可以了

唯一一个需要考虑的变量是栈,毕竟栈里面放着子程序的返回地址,在FC上如果子程序调用层数多了栈可能不够用,区区2K内存分配完了留给栈的可能只有数百字节,当然后期出现了扩展RAM的Mapper,自己动手丰衣足食嘛

等到16bit时代了,SFC有128K内存,MD有64K内存,怎么说都够了

不够?上Mapper啊!

至于你说的那个魂斗罗力量……那是程序写的垃圾的代表作品,完全不是FC的机能极限。君不见烈火92同屏几十个活动快都不带拖慢的,而且还有纵横双轴光栅背景特效……

首先的问题是:怎么定义CPU使用率呢?在带有操作系统的计算机环境下我们一般把某个进程的CPU使用率定义为一段时间内进程运行所消耗的CPU时间的占比。

然而FC游戏运行既没有操作系统可以负责托管空闲CPU时间片,CPU也不支持睡眠功能,那如果游戏1个tick的音画渲染和逻辑处理完了还没到1帧时间怎么办呢?当然是不停地执行无条件跳转耗时间直到下一帧的中断到来(有的游戏是纯粹耗时间,有些游戏会拿这段时间做累加器用来当伪随机数发生器),所以如果以通常的CPU使用率来定义的话那FC游戏的CPU使用率就肯定是100%了。

虽然CPU是被100%占用,但还是可以分「有效CPU使用」和「无效CPU使用」。运行游戏主逻辑的代码都是属于有效使用,而一个tick跑完后拖时间的死循环就可以认为是无效使用。我们可以将有效CPU使用占比视为FC游戏的CPU的使用率。

那么如何测定某一帧时间内CPU的使用率呢?如果在开发的时候一个简单的方法就是把tick跑完之后拖时间的代码改成一个累加器,使得值的增长速度和指令消耗的时钟数同步,这样通过这个计数就可以得知每帧的无效时钟周期数,就能知道CPU使用率了。

利用virtuanes模拟器带上点简单的hack来估计下现有游戏的CPU使用率,就以美版「魂斗罗」为例好了。

我的做法是,把无效CPU使用的代码替换成可以提示当前PPU渲染画面已经渲染到了哪一行了的代码。其实这个实现还比较简单,PPU寄存器0x2001的bit 0是控制当前渲染是彩色渲染还是黑白渲染,所以只要把tick完了最后那个替换成设置黑白渲染模式的代码就行。由于PPU渲染画面是从上到下逐行渲染的,只要看下画面上从哪行开始往下的画面都成了黑白色就知道1 tick跑完之后PPU工作到哪了。

那和CPU是什么关系呢?这个就要知道PPU的工作时序了。

这里就粗略的描述下好了:VBlank NMI中断信号到来(游戏主逻辑开始新tick运行),PPU先是处于「睡眠」状态,持续时间大约占一帧的8%,然后剩下约92%时间就是在逐行渲染了。这里为了简便就不考虑pre render和post render line了。

那么就可知,如果按前述测量方法,某帧的画面全部变成灰色,那么可以认为该帧CPU使用率小于8%。如果画面的彩色部分占比为x%,那么可以认为该帧的CPU使用率大约是(8 + x * 0.92)%。

利用virtuanes debug版运行美版魂斗罗,利用debug找到tick后拖时间的代码(看起来像是个随机数发生器)

然后用普通版本virtuanes打开游戏(debug版CPU频率被人为调快了),按P暂停模拟器,打开内存查看器,找到0xC057处直接修改RAM,把A5 1A 65 34 85 34改成A9 1F 8D 01 20 EA,修改后的二进制码对应的汇编是:

C057: A9 1F LDA $#1F

C059: EA NOP

C05C: 8D 01 20 STA $2001

C05D: 4C 57 C0 JMP $C057

看下效果吧!(需要在设置里打开240线渲染)

游戏开始时大概就在这个位置(会有上下波动),彩色部分占25%,可推算出此帧期间CPU使用率为大约31%。再往后玩,发现第一关过桥到第一个炮台这里CPU消耗很大,已经是90%左右了。

利用单帧步进的方式看下S枪开枪时候的CPU处理消耗吧:

开枪的那一帧CPU消耗高了25%左右,也就是说初始化S弹的5颗子弹要花费7000多个CPU周期。

这些老式游戏机的开发更接近现代的嵌入式开发中单片机部分。按现在的说法叫迭代式结构。加之任务独占,CPU和内存占用率是没有意义的。

迭代式结构是利用程序中的一个死循环,在一次循环内检测所有需要关注的事件,如果该事件发生就执行对应的动作。实现上,可以是直接编写的死循环,也可以是利用硬件Timer产生的具有固定时间间隔的定时器中断来调用。每一次迭代叫做一个tick,所以这种结构也叫tick结构,现代也有叫tick服务器的。

既然是不断的循环,所以自然可以把CPU全部用满。在电脑上的体现就是把CPU跑到100%。一个tick中的任务比较少的时候,那么每秒迭代次数就会比较多。tick中任务比较多的时候,迭代速度就比较慢。但只要没用定时器中断,CPU就是占满的。

Tick结构这种把CPU用满的方式在PC编程上看起来是很蠢的。但在嵌入式开发中却是一种很有效的方式。首先嵌入式CPU如果用不上抢占式多任务的话,CPU就是交给这一个程序去用,闲着也是闲着。而且相对于每种事件一个硬件中断的方式,tick结构不需要每次发生中断时的保护现场(dump所有寄存器),PC指针跳转,恢复现场等工作。所以在事件检测的实时性和可预测性上是很不错的。

在一些实时性和安全性要求较高的应用,比如汽车、航天、宇航等领域。每个子任务的时间必须可预测,且迭代时间可预测。反之中断同时发生、嵌套发生时,响应时间难以预测。所以这些领域的tick结构是很常见的。

内存方面,也是因为通常是独占式任务,连OS都没有。所以嵌入式系统的常见做法是程序启动时就把自己想要用的内存全都分配了,将这些数据都保存为全局变量。这样的结果是程序一启动就基本占满了所有内存,但后续的运行就不会有麻烦的内存申请/释放操作。节省了内存申请/释放带来的CPU占用,而且存储管理本身也是个时间复杂度非线性的功能。

嵌入式开发,包括这些稍微古老的游戏机程序开发。在一开始就有硬件规范,开发者知道CPU的速度,总的内存大小。所以开发中都是倾向于尽量占满CPU和内存,以换取效率的提高。所以可以认为是CPU和内存都是占满的。

应该是没有内存占用率的概念的。

FC和GB等基本上不存在操作系统的层,内存管理由游戏直接进行,一般也就是进行静态分配,哪段内存干啥在游戏设计的时候就进行好分配了,分配到的不管你有没有进行读写都要在那里占着,没分配到的除去程序出错否则自开机到关机都不会用到。

至于CPU占用率,这个倒是有可量化的东西,具体到GB大抵上就是单位时间内CPU处于非HALT或STOP状态下时间的占比。

CPU要与外围硬件进行交互,然而外围硬件的速度相比CPU速度往往较慢,而且不是任何时间都能访问外围硬件。有一种传统的办法是CPU循环检测外围硬件状态,只要合适了就进行访问,这样的方式效率当然低下而且耗能,因此一般采用的是中断处理,即你要与外围硬件交互时,先进行交互前的准备,之后执行HALT,CPU不再执行命令,而是等到硬件发出信号后CPU自动跳转到某个中断程序进行处理,所以HALT时CPU就相当于处于空闲状态。

不过这些都是基于不完善的模拟器使用经验做出的看法,我并没有见过这类游戏的实际开发,更没见过开发机。

这是BGB模拟器中的调试工具,右边中部那个竖条就是CPU占用率。

就实际体验而言,如果是相对合理编写的程序(题外话:GameFreak粪程序!),就算这个条子达到100%,除了长时间这样你会觉得耗电以外,并不会有其他的感觉,因为中断程序的执行能够让声音画面等在合适时间持续进行输出,让你感觉不到什么区别。

魂斗罗力量个人觉得并不是什么机能问题,而是程序质量的问题,大概是将中断关闭去处理某些数据(很有可能是类似于轮询硬件状态的东西),然而关闭的时间过长导致声画变得拖慢诡异。另外 理论上单核CPU,不管你是不是X86都不能同时执行多任务吧,也都是靠中断来完成“多任务”,FC和GB能同时给你显示画面、放音乐、处理游戏其他数据,也是靠中断来进行的,只不过FC和GB等的中断没有X86那么复杂罢了。

突然想到手头有盘FC卡带CPU负荷显示的。

可能知道的人不多,今年年初发行的,纯音乐播放软件。具体各位百度吧。

由于改卡带ROM没有公开dump,以下照片为实机。

游戏标题

Option里,可以看到select可以显示CPU负荷

正常播放界面

打开CPU符合展示后,画面出现类似半透明效果的动态来表示CPU负荷

游戏ROM的工程版本中基本上都会有相关的DEBUG菜单,主要是用来做一些功能调试游戏测试的,其中不少DEBUG菜单中包含显示了硬件使用率,RAM,ROM,CPU,PPU使用率,帧率等。一些基本功能不需要额外附加的硬件。这些代码功能在正式发售的游戏里会被剔除或者做简单的屏蔽。

FC,SFC,MD,街机等正式零售的ROM版本中仍然包含了大量的DEBUG功能。比如街机的很多调试功能可以通过框体基板上的DIPS开关来开启。家用机游戏的部分ROM DEBUG菜单可以使用硬件金手指开启。模拟器上也可以。

当然,显示方式并不如windows任务管理器那样直观....基本都是16进制数值表示的....

例子不胜枚举....

MD Disney's Aladdin

零售版ROM包含DEBUG菜单

RAM和ROM使用率显示在此。SFC Star Fox 2工程版

此游戏因故取消了开发和发售,网上可以找到工程版。工程版最明显的特征是左上角实时显示游戏帧率。此游戏可以用正版Star Fox一代卡带替换烧录ROM在实机上使用。

FC Toxic Crusaders

零售版ROM隐藏DEBUG菜单

CHR内存和活动块相关的地址... 老游戏机都是数指令周期的,内存也是全部静态分配。

Atari 2600 内存只有 0.1K,每个字节甚至都要考虑复用

发表评论
留言与评论(共有 0 条评论)
   
验证码:

相关文章

推荐文章

'); })();