一、启动过程
当gameboy通电后,机子会从内存地址为0的地方开始运行一段长度为256字节的程序,这段程序是固化在gameboy内部的ROM(只读存储器)上的。
这段程序的作用是,把卡带中从104H到133H地址的任天堂的LOGO读取出来并显示在屏幕的最上方。这个LOGO会滚动到屏幕中间,然后会播放两个提示音。然后会把从这个地址段读出来的数据和内部数据进行比较,如果比较失败,则停止运行。如果比较通过,则把地址从134H到14DH的数据逐个相加,再把相加的结果加25,如果最后得到的结果的最低有效位不为0,则停止运行。否则,内部程序运行结束,机子会从卡带地址100H处开始执行实际的游戏指令,同时设置寄存器为以下值:
AF=$01B0
BC=$0013
DE=$00D8
HL=$014D
Stack Pointer=$FFFE
[$FF05] = $00 ; TIMA
[$FF06] = $00 ; TMA
[$FF07] = $00 ; TAC
[$FF10] = $80 ; NR10
[$FF11] = $BF ; NR11
[$FF12] = $F3 ; NR12
[$FF14] = $BF ; NR14
[$FF16] = $3F ; NR21
[$FF17] = $00 ; NR22
[$FF19] = $BF ; NR24
[$FF1A] = $7F ; NR30
[$FF1B] = $FF ; NR31
[$FF1C] = $9F ; NR32
[$FF1E] = $BF ; NR33
[$FF20] = $FF ; NR41
[$FF21] = $00 ; NR42
[$FF22] = $00 ; NR43
[$FF23] = $BF ; NR30
[$FF24] = $77 ; NR50
[$FF25] = $F3 ; NR51
[$FF26] = $F1-GB, $F0-SGB ; NR52
[$FF40] = $91 ; LCDC
[$FF42] = $00 ; SCY
[$FF43] = $00 ; SCX
[$FF45] = $00 ; LYC
[$FF47] = $FC ; BGP
[$FF48] = $FF ; OBP0
[$FF49] = $FF ; OBP1
[$FF4A] = $00 ; WY
[$FF4B] = $00 ; WX
[$FFFF] = $00 ; IE
二、Rom文件的反汇编
好了,现在知道了ROM的文件结构(参考上一篇文章),知道了gameboy从ROM的什么地方开始执行指令,接下来就要对ROM进行反汇编,看看ROM的实际内容是什么东西。
先说一下反汇编的思路:首先按字节从ROM文件中读取单个字节的数据,然后根据gameboy的CPU指令手册,把读出来的数据翻译成相应的CPU指令输出成文本,这样就能看到ROM的内容了。
有了思路之后我们就开始着手实现吧,嘿嘿嘿,首先需要一个gameboy的CPU指令手册,可以从网上找到相应的资料。这个是我找到的CPU的参考资料:http://www.myquest.nl/z80undocumented/z80cpu_um.pdf
从资料中可以知道,gameboy用的CPU是8位的,从程序的角度讲,就是CPU一次能从内存中读取8个位,刚好一个字节的数据到内部,所以我们要逐个字节分析,接下来这个是gameboy的CPU所用到的指令:http://pastraiser.com/cpu/gameboy/gameboy_opcodes.html
从表格中我们可以知道每条指令的长度,以及每条指令对应的二进制数,而且我们看可以看到有些指令是用一个字节表示,有些指令是用两个字节表示,而且两个字节的指令都是以十六进制数CB开头的。注意:一条完整的指令应该包括指令的操作符和操作数,这里说的一个字节、两个字节只是用来表示指令的操作符,不包含操作数,所以完整的指令长度还应该包含标识指令操作数的长度。我们可以先获取到指令的操作符,然后再根据地址中给出的表格来查询出指令的操作数以及完整指令的指令长度。
对于两个字节长度的操作符,CPU会先读第一个字节,如果第一个字节是CB的话,则再读下一个字节,这样根据第二个字节读到的值,就可以通过查表来确定该两个字节长度的操作符是什么了。
那么CPU怎么知道它读出来的就一定是操作符,而不是操作数或别的什么东西呢。
首先,CPU一定是从读取第一个指令的操作符开始的,然后通过翻译该操作符,再获取到这条指令使用到的操作数。接着执行该条指令,执行完后再去读取下一个指令。所以这就要求CPU在翻译指令的时候,最先读取出来的一定是指令的操作符,这样才能确定该条指令的长度,以及实用那些操作数等这些指令的附加信息。
所以,我们在模拟gameboy的CPU进行翻译指令的时候(也就是通常所说的译码),首先读取进来的一定是CPU的指令的操作符,这时如果读取的值是CB,则我们就可以知道当前处理的是一个双字节长度的操作符,接着我们需要读取下一个地址的字节数据,然后通过这次读取出来的数据来确定当前世纪的操作符是什么。










网友评论