溢出关卡吧 关注:250贴子:3,572

关于在SMB1改版中SRAM使用的问题

只看楼主收藏回复

一楼百度


IP属地:贵州1楼2021-02-15 15:09回复
    在8年前,已经是分析过了SMB1中内存的分布,其中$6000~$7FFF就是SRAM,无效内存,读取到的只有四种情况:00,FF和用来读取地址的高(低)字节,但是个人认为SRAM的这个区域6000-7FFF在其他很多游戏里都是有具体数值的,有些是作为类似RAM的那种可读写区域。主要是在两年前我下载了一个b站上的SMB改版,它的ROM有80KB,它的新增和更改内容比较像SMB2J的那一些(倒立水管,毒蘑菇,刮风之类的),但也不是FDS的;虽然GFX是2J的,但关卡数只有和初代相同的36关。(后来发现就是15年在贴吧上发布的超级玛丽加难版V6.3)但最令人不解的是,它运行的时候在$6000~$7FFF塞了一堆可运行代码(只不过大部分都是EA,NOP),$8000~$FFFF又都是ROM部分,而且从运行效果来看,它没有切过bank。所以这一切到底是因为什么?ROM多了16KB又少了这么多要素?SRAM成了ROM的一部分?虽然在FDS中是会用到这一部分的,但也只是把映射进内存的ROM从$8000~$FFFF移动到了$6000~$DFFF,也不是ROM部分扩展了......求大佬解答(3楼我会放一些图片和下载地址)(4楼还有几个问题)


    IP属地:贵州2楼2021-02-15 15:09
    回复
      b站上这个作者的原视频在av42694939上,附带下载地址的。另外补充一下,这个改版只有三个跳关区,不是2J的那种。


      有倒立水管,倒立食人花和毒蘑菇...

      就从这一个跳关区能看出来,这里没有拆分什么跳关区。

      有一些可执行代码(?)

      清一色的EA...

      ROM部分也是使用正常的


      IP属地:贵州3楼2021-02-15 15:13
      回复
        说到SRAM我就想到SMB3...之前在b站上看到对"Wrong Warp"速通的解析视频(av797083879),顺便把这个速通的前面也分析了一番。但我看到里面有几个迷惑问题:
        1.和上面这个hack类似,在$6000~$7FFF也塞了一堆数据,但是在曾经有一个视频(av69783940)里说的是WorkRAM.....?这和SMB1的SRAM为什么会有区别呢?
        2.在RTS到$0081后,视频中特意提到那个矫正bank的BRK。SMB1中这玩意会导致崩溃,但SMB3却不,不仅没有崩溃,还用了两个字节......明明第一个字节都是00,为什么SMB3就加了一个毫无意义的字节呢?(在曾经那个视频里是BRK #$XX,新出了一种BRK的方法?)(后面有一个很奇怪的74,居然还说是NOP...)难不成还有第二个6502汇编的版本?


        IP属地:贵州4楼2021-02-15 15:13
        回复
          先说一下SRAM这个概念,它应该是Static(静态)RAM的意思,通常是相对于DRAM(D是Dynamic的首字母,意为“动态”)来说的;这个词出现于VirtuaNES模拟器当中,对应的内存地址为$6000~7FFF,而一些其他模拟器(例如Mesen)当中,这个区域则被称为“工作RAM”,因此SRAM和工作RAM其实是一回事。
          然后,在原版(NES版)SMB1当中,SRAM是未使用的内存,而移植到FDS之后,SRAM的区域就是正常用到的内存了,这是为了给那2KB的磁碟机操作系统(Disksys.rom)腾地方的,而且这个场合下,这个区域的内存就不应该称为SRAM了,实际上,对于FDS,$6000~DFFF这个范围全部都是RAM(根据实际储存的内容,可以称为PRG RAM。)再然后,由于SMB2J只有FDS版本,因此有人设法把2J移植到了卡带上,但是移植之后的运行特性仍然与FDS的原版相同,保留了Disksys.rom的内容(嵌入到移植之后的ROM当中了),并且一部分程序放入了SRAM的区域,还有一部分会改写内容的程序区域(原版2J当中是$C000~DFFF里面的部分区域),则将这些不同的内容分成几个bank,通过切bank的方式实现改变内容。所以,SMB2J的卡带移植版比原版体积还要大(原版整张单面磁碟约为64KB,而最小的NES移植版也有72KB)。
          再就是你说的这个改版了,我本来以为它应该也是用了类似的方式进行的修改(直接以某个NES移植版的2J为模板),但是看了你发的两张代码图(除去全是EA那个),发现应该是这样改的:把1代原版扩容之后,在新增的bank中添加了2J元素的程序(图中有一些FF,这应该就是把添加的程序按16字节对齐排列之后的结果),这些程序在运行时写入到了SRAM的区域。为什么这么说呢?比对你的第二张代码图(还是不算全是EA那个),跟SMB1原版相同地址的程序完全一致
          最后再说说其他游戏对SRAM的使用情况……好吧,我也不是很清楚,只能说SRAM就是在原有的RAM基础上扩充的一块内存。(嗯,比原有的RAM大,一样可以当RAM用,不过还不够大——MMC5还提供了一块更大的扩展内存。)至于我知道的用到SRAM的游戏(其实也仅限于SMB的范畴了),除去FDS版本的游戏不算,VS版SMB1用SRAM储存关卡中用于生成地形和敌人的数据,以及其他大量我不知道是干啥的数据();SMB3用SRAM储存屏幕上的地形相关数据;其他我就不知道了。
          最后的最后,关于汇编指令的问题。
          BRK在不同的游戏中处理方式是不同的(正常情况下,是用于开发过程的调试指令),对于NES版SMB1是会导致崩溃的指令,对于FDS游戏则有内置的“处理并返回”措施,而对于用到IRQ的游戏,由于IRQ与BRK指令共享IRQ向量,因此通常来说BRK之后可以正常返回。不过注意BRK返回的位置,是BRK之后的第二个字节(相当于屏蔽了BRK之后的一个字节),因此在写指令时,可以写成BRK #$xx,把屏蔽的这个字节表示出来。
          至于74这个指令码,它是一个未定义指令码,不同模拟器的调试器中对未定义指令码的表示方法也不同。VirtuaNES中,用DOP表示指令码74以及其他若干(但不是全部)2字节的未定义指令码,此外还有NOP表示1字节未定义指令码(与正常NOP重名警告),TOP表示3字节未定义指令码,还有一堆奇怪的名字(SLO、RLA、DCP、ISB等);Mesen也有类似的命名规则,而且会把指令码后面的操作数也一并写到指令名后面(VirtuaNES则没有);FCEUX则偷懒把未定义指令全部命名为UNDEFINED,而且全部假定为1字节指令。不过用NOP表示74……这似乎有点不太合理了。


          IP属地:上海5楼2021-02-15 19:26
          收起回复
            首先非常感谢大佬解答!我之前就一直以为SRAM=unuSeRAM,但是后面看了别的游戏就发现不太对了。这是一块可以当RAM用的区域,但我听说某些游戏的SRAM是拿来存一些“静态数据的”(比如VS版SMB1存的最高分数,可能不是存在这里的),存储一些断电也还在的数据(av56456428 09:40)(这是那个视频的截图)。

            关于这个改版...我之前翻了一下这个作者之前发布的游戏,好像这个hack在V6.0以前都是40KB的原版引擎,到V6.0才改成这种特殊的引擎。另外,它加进来的这些物品跟2J一样,打乱了原版物品的排序。详细一点说,地形中纵坐标为0~B的地形单位,原版用的上的是在00~0B。而2J把三种毒蘑菇(隐藏砖,问号砖,普通砖)和隐藏蘑菇砖分别插入到了01,05,06和08,导致原本的01~03往后推1个ID,04往后推3个,05~0B往后推了4个。

            那个把74指成NOP的视频在上面av797083879,可以去b站翻一下原视频(不过是机翻,应该不用字幕也看得懂)。我这里截了几张图。


            最扯的是,这个NOP占两个字节,还写着NOP($74),X,我怀疑这个NOP指令本体在88这个字节上...
            我在贴吧翻这些资料没到半年,对汇编这一块肯定一窍不通。最后,FDS中那个$6000~$DFFF好像是ROM,PRG ROM吧,还有disksys.rom好像是8KB...


            IP属地:贵州6楼2021-02-15 20:42
            回复
              回复6楼:
              先说那个Disksys.rom吧,确实是8KB,RAM才是2KB,手滑了……
              关于SRAM,貌似有些游戏是用来记录存档的(电池记忆)。
              你说的这个改版,我推测可能是用别人扩容并写入了2J元素程序的1代改版为模板修改的(比如有个这样的改版是Insectduel制作的,也是80KB),至于你那张表,我也整理过的
              至于视频里的NOP ($74),X,这个指令写法就不科学,88哪去了?如果按照VirtuaNES的命名,这个指令应该是DOP,再结合这个位置的指令的寻址方式,应该写成DOP $88,X(没有括号)。另外,关于未定义指令,虽然它们没有定义,但是执行这种指令也会对寄存器的值产生影响(具体不明)。
              关于FDS中的PRG RAM(无误),地址确实为$6000~DFFF这个范围,并且运行过程中,这块内存中的值是可以随时改变的,2J程序中有大量的这种骚操作(比如人物切换时,进入游戏之后的人名和颜色都是重写过的)


              IP属地:上海7楼2021-02-15 21:20
              收起回复
                我记得有一次我用FCEUX监控PPU内存时不但没监控到PPU的那部分,反而看到了几条条让我怀疑人生(?)的指令:LDA #$00;STA $4000;STA $4004;这是什么情况......$4000-5FFF不是无效内存吗,而且直接看内存里$4000-4FFF也是清一色的FF...


                IP属地:贵州来自Android客户端8楼2021-02-16 08:14
                收起回复
                  现在我感觉那个SMB3的视频全是误导...或者说作者没有收集够这些汇编语言...
                  因为后面提到如果那个“NOP”的74(这里指的是游戏中1号敌人的横坐标)慢两帧变成72...就是KIL???不过感觉没毛病,模拟器都崩了,肯定就是强行终止了

                  所以我感觉有理由怀疑这个73是不是别的了(?)

                  主要我也不会翻译...Illegal opcode是什么东西啊


                  IP属地:贵州9楼2021-02-16 09:12
                  收起回复
                    想起关于SMB2JGFX的问题...
                    众所周知,2JGFX中数字和字母是有阴影的,但这导致了数字的GFX重构了一次。因为在分数栏显示大关号的时候,正常显示的像素只有8x7,最下面的一排在SMB1里会抽搐(?),就拿37-1举例。


                    注意大关号,图一中的大关号最下面一排有些错位。这说明SMB1的卷轴拆分在屏幕显示范围内只有23像素,而非24像素,那一排可能就是“半错位”。但是情况到了2J,就有些不同了。
                    重构了数字以后,不带阴影的高为6像素,带上阴影也只有7像素,但是字母没有重构,阴影就到了第8行。但好像因为某个独特FDS的特性,这一排像素就是“完全错位”。


                    第四张图那个A字母,最下面的阴影已经错位的离谱了。
                    但是到了NES2J,就又变成SMB1的情况了。

                    那么问题来了,这个FDS和NES的差异是哪里产生的?


                    IP属地:贵州10楼2021-02-16 17:28
                    收起回复
                      回复10楼:
                      《关于卷轴拆分的两种方式.docx》
                      (最近刚写的,全文抄录如下)
                      目前,我所知道的卷轴拆分的方式有两种,分别用于SMB1和SMB2J当中;像SMB3那样底部固定式的拆分方法,我就不清楚原理了,这里也不打算讨论。
                      SMB1使用的卷轴拆分技术比较原始但稳定有效,它是利用了“0号精灵”这个特殊的精灵来实现的,很多早期的FC游戏都用到了这个技术。其原理为:FC对0号精灵有一个特殊的检测,依次检测精灵的每个不透明像素是否与背景层的不透明像素重合,如果检测到了一个这样的重合像素,则称该检测“命中”了(0号精灵命中),并且会在PPU寄存器中留下一个标记:将$2002的位6(最低位为位0,最高位为位7,位6即为左数第2位)从0变为1。因此,只要合理设置0号精灵的位置,在0号精灵命中之前把命名表窗口固定在最左边,命中之后再把窗口移动到实际需要显示的位置,就实现了卷轴拆分。具体到SMB1的情况,0号精灵被很巧妙地设计成了状态栏中的小金币下方的阴影,被小金币挡住了一部分,这样就可以在准确的时间发生0号精灵命中。这个命中时机不会受制式影响,因此如果游戏在PAL制式运行,卷轴拆分也不会出错。
                      如果游戏运行过程中发生某些状况,导致0号精灵或者它所在的背景层图块发生改变,轻则卷轴拆分错位,重则0号精灵无法命中,从而使判断$2002的值的循环无法结束,陷入死循环。这种死循环的状况,我将其称为SP0崩溃,实际上是LOOP崩溃(死循环)的一种,但由于崩溃原因明确,因此单独拿出来命名。
                      再说SMB2J使用的卷轴拆分技术。首先我们可以注意到,SMB2J的小金币没有阴影了,这也就意味着2J不再使用0号精灵了;其次,如果以PAL制式运行SMB2J,卷轴拆分会失效,状态栏会跟着地形一起滚动。这种现象,倒是让我想起了之前看过的一个帖子,那里提到了恶魔城传说这个游戏,在PAL制式下会出现状态栏花屏的现象,如图:

                      经过本人验证,在PAL制式下,恶魔城传说的状态栏也是跟着地形滚动的,跟SMB2J一样。因此我有理由怀疑,恶魔城传说跟SMB2J用的是同一种卷轴拆分技术:定时拆分。简单说,就是在每一帧的特定时间,显示完固定在命名表左边的状态栏内容之后,将窗口移到命名表的实际显示位置,实现卷轴拆分。在恶魔城传说中,除了移动窗口位置,还要切换用于显示的CHR bank,实现状态栏和地图使用不同的贴图组;而SMB2J就没有切CHR bank的需求,就算有,以磁碟机的技术限制,每次切换数据要等7秒时间,这可等不起……
                      既然是定时的,那么用于定时的依据是什么呢?根据贴吧大神Airwe_的说法,这个依据就是NMI,当NMI发生一定时间之后,屏幕显示正好进行到状态栏显示完毕的时刻,此时对命名表窗口的位置进行调整,即可正常实现卷轴拆分。而NTSC和PAL制式下,NMI的发生时机是不同的,于是NMI之后经过相同的时间,NTSC下正好显示完状态栏的时候,PAL还没开始显示,然后卷轴拆分就失效了,同时还造成了恶魔城传说的状态栏花屏。


                      IP属地:上海11楼2021-02-16 17:48
                      收起回复
                        剩下一堆和SRAM毫无关系的问题
                        众所周知,2J的元素大体上还是和SMB1一样的,只是加了一些或是改了一些,空间传送这一块没有改。从空间传送这个敌人来说,应该只能在1-8世界生效,9-D世界根本不生效。但2J里每个世界都可以生效,这说明9-D世界就不是真正的9-D世界,目前我大概知道A-D世界相当于空间传送中用的1-4世界,但第九世界仍是个谜......这让我想起之前这个吧发过的一个SMB1hack,也是80KB,1-8世界和原版基本相同,但又拼凑上去了9-D世界,原版E-1还移到了I-1的那个...有次我开金手指075F:FF(FCEUX),发现空间传送还居然生效了!事情可就没有只是改9-D世界的真实世界数这么简单了!现在那个帖子好像还在,一楼就是说试着开这个hack075F 08的那个。这是直接改了空间传送的机制?好像也不对,要是直接舍弃世界数1-1钻管子上来可能就是7-1了...


                        IP属地:贵州来自Android客户端12楼2021-02-17 07:41
                        收起回复


                          我已经懒得吐槽百度了……不打算申请恢复,直接发截图


                          IP属地:上海14楼2021-02-17 20:27
                          收起回复
                            这个改版还有很多离谱的东西
                            跳关区域貌似有扩展区域(8 7 6,36 5 36,4 3 2,36 11 36),A-2可以跳B-1,但如果在超过D世界跳关就有这些奇怪的关卡(这个ROM我自己改过GFX)




                            这又是什么鬼?

                            梅开二度

                            这调色板怎么这么奇怪...


                            IP属地:贵州15楼2021-02-17 21:52
                            收起回复
                              既然提到跳关区了,就比较一下这两种跳关区的程序吧。
                              1代原版的跳关区信息显示过程如下:先判断$075F,如果是0(第1世界),就令A=4;否则(非第1世界)如果$074E=1(地上),就令A=6,否则(非地上)令A=5。然后根据A减去4的值(可以是0~2),选择对应的跳关区数字显示到水管位置。
                              而这个改版则改成了这样:首先令Y=$075F,然后从$6A60出发,偏移Y个字节读取数值存入X,如果X不为0(感觉这里是改版作者写错了),则当$074E=1时(地上),令X加1,否则(非地上)X值不变;再将X的值存入A……后面程序的执行过程感觉有点复杂,不过略过一部分之后,最后跟原版好像是一样的:根据A减去4的值,选择对应的跳关区数字显示到水管位置。
                              这个过程说明了什么?说明每个世界都对应一组跳关区(或者两组,根据$074E的不同可以有区别)正常世界的跳关区数据共有5组,对应取值4~8,而溢出世界的跳关区自然也是溢出的取值了。至于S世界的跳关区看上去还相对“正常”,是因为正好读取了一个正常取值5。


                              IP属地:上海16楼2021-02-17 23:24
                              收起回复