3

我目前正在编写 Gameboy 模拟器,我注意到一些对我来说似乎很奇怪的东西。

0xCD例如,我的模拟器正在执行跳转指令,CD B6 FF但我的理解是跳转应该只跳转到盒式 ROM 内的地址(0x7FFF最大值),因为我假设 CPU 只能执行来自 ROM 的指令,而不是 RAM。有问题的 ROM 是 Mario 博士,我希望它只会执行有效的操作。0xFFB6在高 RAM 中,这对我来说似乎很奇怪。

我的想法正确吗?如果我是,大概这意味着我的程序计数器以某种方式以错误的地址结束,并且CB实际上是另一条指令数据的一部分,而不是指令本身?

我将不胜感激,谢谢。

作为参考,我一直在使用Gameboy OpcodesCPU 文档来实现指令。我知道它们包含一些错误,我想我已经解释了它们(例如,0xE2 被列为两字节指令,而它只有一个)

4

2 回答 2

3

刚刚检查了 Dr. Mario 1.1,它在启动时复制 hFFB6 处的 VBlank int 例程,然后当 VBlank 发生时,调用 0:01A6 处的例程,该例程调用 OAM DMA 传输例程。

在 OAM DMA 传输期间,CPU 只能访问 HRAM,因此需要在 HRAM 中编写一个等待传输完成的短例程。OAM DMA 传输需要 160 µs,因此您通常会创建一个循环,在指定 OAM 传输源后等待这段时间。

这是在启动时运行的初始化例程的一部分,它将 DMA 传输例程复制到 HRAM:

...
ROM0:027E 0E B6            ld   c,B6             ;destination hFFB6
ROM0:0280 06 0A            ld   b,0A             ;length 0xA
ROM0:0282 21 86 23         ld   hl,2386          ;source 0:2386
ROM0:0285 2A               ldi  a,(hl)           ;copy OAM DMA transfer routine from source
ROM0:0286 E2               ld   (ff00+c),a       ;paste to destination
ROM0:0287 0C               inc  c                ;destination++
ROM0:0288 05               dec  b                ;length--
ROM0:0289 20 FA            jr   nz,0285          ;loop until DMA transfer routine is copied
...

当 VBlank 发生时,它会跳转到 0:01A6 的例程:

ROM0:0040 C3 A6 01         jp   01A6

其中包含对我们的 OAM DMA 传输例程的调用,等待 DMA 完成:

ROM0:01A6 F5               push af
ROM0:01A7 C5               push bc
ROM0:01A8 D5               push de
ROM0:01A9 E5               push hl
ROM0:01AA F0 B1            ld   a,(ff00+B1)
ROM0:01AC A7               and  a
ROM0:01AD 28 0B            jr   z,01BA
ROM0:01AF FA F1 C4         ld   a,(C4F1)
ROM0:01B2 A7               and  a
ROM0:01B3 28 05            jr   z,01BA
ROM0:01B5 F0 EF            ld   a,(ff00+EF)
ROM0:01B7 A7               and  a
ROM0:01B8 20 09            jr   nz,01C3
ROM0:01BA F0 E1            ld   a,(ff00+E1)
ROM0:01BC FE 03            cp   a,03
ROM0:01BE 28 03            jr   z,01C3
ROM0:01C0 CD B6 FF         call FFB6             ;OAM DMA transfer routine is in HRAM
...

OAM DMA 传输例程:

HRAM:FFB6 3E C0            ld   a,C0
HRAM:FFB8 E0 46            ld   (ff00+46),a      ;source is wC000
HRAM:FFBA 3E 28            ld   a,28             ;loop start
HRAM:FFBC 3D               dec  a
HRAM:FFBD 20 FD            jr   nz,FFBC          ;wait for the OAM DMA to be completed
HRAM:FFBF C9               ret                   ;ret to 0:01C3
于 2017-02-07T03:02:14.137 回答
0

以下是我的分析:

  1. 在原始 ROM 中查找CD B6 FF,我只能在内存的一个位置找到它0x01C0(十进制的 448)。

  2. 所以我决定反汇编ROM,看看它是否是一条有效的指令。

我使用gb-disasm来反汇编 ROM。以下是从0x150(ROM start) 到 address的值0x201

[0x00000100] 0x00           NOP
[0x00000101] 0xC3 0x50 0x01 JP $0150
[0x00000150] 0xC3 0xE8 0x01 JP $01E8
[0x00000153] 0x01 0x0E 0xD0 LD BC,$D00E
[0x00000156] 0x0A           LD A,[BC]
[0x00000157] 0xA7           AND A
[0x00000158] 0x20 0x0D      JR NZ,$0D ; 0x167
[0x0000015A] 0xF0 0xCF      LDH A,[$CF] ; HIMEM
[0x0000015C] 0xFE 0xFE      CP $FE
[0x0000015E] 0x20 0x04      JR NZ,$04 ; 0x164
[0x00000160] 0x3E 0x01      LD A,$01
[0x00000162] 0x18 0x01      JR $01 ; 0x165
[0x00000164] 0xAF           XOR A
[0x00000165] 0x02           LD [BC],A
[0x00000166] 0xC9           RET
[0x00000167] 0xFA 0x46 0xD0 LD A,[$D046]
[0x0000016A] 0xE0 0x01      LDH [$01],A ; SB
[0x0000016C] 0x18 0xF6      JR $F6 ; 0x164
[0x000001E8] 0xAF           XOR A
[0x000001E9] 0x21 0xFF 0xDF LD HL,$DFFF
[0x000001EC] 0x0E 0x10      LD C,$10
[0x000001EE] 0x06 0x00      LD B,$00
[0x000001F0] 0x32           LD [HLD],A
[0x000001F1] 0x05           DEC B
[0x000001F2] 0x20 0xFC      JR NZ,$FC ; 0x1F0
[0x000001F4] 0x0D           DEC C
[0x000001F5] 0x20 0xF9      JR NZ,$F9 ; 0x1F0
[0x000001F7] 0x3E 0x0D      LD A,$0D
[0x000001F9] 0xF3           DI
[0x000001FA] 0xE0 0x0F      LDH [$0F],A ; IF
[0x000001FC] 0xE0 0xFF      LDH [$FF],A ; IE
[0x000001FE] 0xAF           XOR A
[0x000001FF] 0xE0 0x42      LDH [$42],A ; SCY
[0x00000201] 0xE0 0x43      LDH [$43],A ; SCX
  1. 我们必须按照指令流来反汇编 ROM。例如,我们知道主程序从位置开始0x150。所以我们应该从那里开始拆卸。然后我们一个接一个地按照指令进行操作,直到我们命中任何JUMP指令(JPJRCALLRET等)。从那一刻起,程序流程分为两部分,我们应该按照两条路径进行反汇编。

这里要理解的是,如果我向您展示 ROM 中的随机存储器位置,您无法告诉我它是数据还是指令。找出答案的唯一方法是遵循程序流程。我们需要定义以跳转目标开始并以另一条跳转指令结束的代码块。

  1. gb-disasm 会跳过任何不在代码块内的内存位置。0x16C标记一个块的结束。

    [0x0000016C] 0x18 0xF6 JR $F6 ; 0x164

下一个区块开始于0x1E8。我们知道,因为它是位于 上的跳转的目标地址0x150

[0x00000150] 0xC3 0xE8 0x01 JP $01E8
  1. 0x16E直到的内存块0x1E8不被视为代码块。这就是为什么您看不到0x01C0作为指令列出的内存位置的原因。

因此,您很可能以错误的方式解释说明。如果你想 100% 确定,你可以拆开整个房间,检查是否有任何指令指向0x16E-0x1E8原始数据并将其读取为原始数据,例如瓷砖或其他东西。

如果您同意分析,请发表评论。

于 2017-02-05T20:24:11.147 回答