DMG-acid2测试ROM失败
我在用C 语言开发一个Game Boy仿真器,并使用DMG-acid2测试ROM来测试PPU。其他的blargg测试ROM都通过,但PPU的渲染存在问题。具体表现为眩动和笑脸的一部分缺失。我附上了一段视频以作进一步展示。
我尝试查看PanDocs,看看是否有我遗漏的地方,但找不到导致我的PPU出现这种情况的原因。
void tick_ppu(Gameboy *gameboy, uint8_t cycles) {
if (!(gameboy->ppu.lcdc & 0x80)) {
gameboy->ppu.ly = 0;
gameboy->ppu.mode = 0;
gameboy->ppu.dots = 0;
return;
}
gameboy->ppu.dots += cycles;
switch (gameboy->ppu.mode) {
case 2: {
if (gameboy->ppu.dots >= 80) {
gameboy->ppu.sprite_count = 0;
uint8_t sprite_height = (gameboy->ppu.lcdc & 0x04) ? 16 : 8;
for (int i = 0; i < 40 && gameboy->ppu.sprite_count < 10; i++) {
uint8_t y = gameboy->oam[i * 4];
int16_t sprite_y = y - 16;
if (gameboy->ppu.ly >= sprite_y && gameboy->ppu.ly < sprite_y + sprite_height) {
for (int j = 0; j < 4; j++) {
gameboy->ppu.sprite_buffer[gameboy->ppu.sprite_count * 4 + j] = gameboy->oam[i * 4 + j];
}
gameboy->ppu.sprite_count++;
}
}
gameboy->ppu.mode3_length = 172 + (gameboy->ppu.sprite_count * 8);
gameboy->ppu.dots = 0;
gameboy->ppu.mode = 3;
}
break;
}
case 3: {
if (gameboy->ppu.dots >= gameboy->ppu.mode3_length) {
gameboy->ppu.dots = 0;
gameboy->ppu.mode = 0;
render_scanline(gameboy);
}
break;
}
case 0: {
if (gameboy->ppu.dots >= 204) {
gameboy->ppu.dots = 0;
gameboy->ppu.ly++;
if (gameboy->ppu.ly >= 144) {
gameboy->ppu.mode = 1;
gameboy->if_reg |= 0x01;
} else {
gameboy->ppu.mode = 2;
}
}
break;
}
case 1: {
if (gameboy->ppu.dots >= 456) {
gameboy->ppu.dots = 0;
gameboy->ppu.ly++;
if (gameboy->ppu.ly >= 154) {
gameboy->ppu.ly = 0;
gameboy->ppu.mode = 2;
gameboy->ppu.window_line = 0;
}
}
break;
}
}
gameboy->ppu.stat = (gameboy->ppu.stat & 0xFC) | (gameboy->ppu.mode & 0x03);
if (gameboy->ppu.ly == gameboy->ppu.lyc) {
gameboy->ppu.stat |= 0x04;
if (gameboy->ppu.stat & 0x40) {
gameboy->if_reg |= 0x02;
}
} else {
gameboy->ppu.stat &= ~0x04;
}
}
If you want a further view, 以下链接可查看源代码提交哈希对应的内容:
render_scanline - https://git.critchlow.net/brodycritchlow/CGameboy/src/commit/b122b11203eb2fe0c62afb4eaac81f80f740b618/cpu.c#L2652
init_display - https://git.critchlow.net/brodycritchlow/CGameboy/src/commit/b122b11203eb2fe0c62afb4eaac81f80f740b618/gameboy.c#L42
解决方案
最终有效的修复方法如下:
在渲染扫描线时,我选择创建一个新的“精灵缓存”(sprite buffer),本质上是一个用于渲染所需对象的缓存。这使我能够摆脱实时OAM扫描,这是代码中的一个压力点。
for (int i = 0; i < count; i++) {
sprites[i].y = gameboy->ppu.sprite_buffer[i * 4];
sprites[i].x = gameboy->ppu.sprite_buffer[i * 4 + 1];
sprites[i].tile = gameboy->ppu.sprite_buffer[i * 4 + 2];
sprites[i].flags = gameboy->ppu.sprite_buffer[i * 4 + 3];
sprites[i].oam_index = i;
}
在 tick_ppu 中,总扫描线数量因为模式定时0 而不正确:
case 2: {
if (gameboy->ppu.dots >= 80) {
gameboy->ppu.dots = 0;
gameboy->ppu.sprite_count = 0;
uint8_t sprite_height = (gameboy->ppu.lcdc & 0x04) ? 16 : 8;
for (int i = 0; i < 40 && gameboy->ppu.sprite_count < 10; i++) {
uint8_t y = gameboy->oam[i * 4];
int16_t sprite_y = y - 16;
if (gameboy->ppu.ly >= sprite_y && gameboy->ppu.ly < sprite_y + sprite_height) {
for (int j = 0; j < 4; j++) {
gameboy->ppu.sprite_buffer[gameboy->ppu.sprite_count * 4 + j] = gameboy->oam[i * 4 + j];
}
gameboy->ppu.sprite_count++;
}
}
gameboy->ppu.mode3_length = 172 + (gameboy->ppu.sprite_count * 8);
gameboy->ppu.mode0_length = 456 - 80 - gameboy->ppu.mode3_length;
gameboy->ppu.dots -= 80;
将所有多余的点保留,而不是设为0,下面给出前后对比的示例:
if (gameboy->ppu.dots >= 80) {
gameboy->ppu.dots = 0;
...
}
// AFTER
if (gameboy->ppu.dots >= 80) {
gameboy->ppu.dots -= 80;
...
}
现在它可以在没有任何缺失的画面碎片或视觉闪烁的情况下通过DMG-acid2测试。
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。
