From 940dc69ae567bd8538fb61835e69b6276e831aaf Mon Sep 17 00:00:00 2001 From: YoshiRulz Date: Fri, 28 May 2021 15:32:42 +1000 Subject: [PATCH] Add regression tests for GB/C using various testroms --- .gitignore | 3 +- ...awk.Tests.Testroms.GB.GambatteSuite.csproj | 14 + .../GambatteSuite.Cases.cs | 1374 +++++++++++++++++ .../GambatteSuite.cs | 226 +++ .../Properties/AssemblyInfo.cs | 3 + .../readme.md | 15 + .../res/download_from_ci.sh | 4 + .../run_tests_debug.sh | 2 + .../run_tests_release.sh | 2 + .../.download_from_ci.sh | 23 + .../.run_tests_with_configuration.sh | 8 + .../BizHawk.Tests.Testroms.GB.csproj | 29 + .../DummyFrontend.cs | 242 +++ .../GB_GBC/AcidTestroms.cs | 126 ++ .../GB_GBC/BullyGB.cs | 85 + .../GB_GBC/GBHelper.cs | 161 ++ .../GB_GBC/MealybugTearoomTests.cs | 265 ++++ .../GB_GBC/RTC3Test.cs | 119 ++ src/BizHawk.Tests.Testroms.GB/ImageUtils.cs | 104 ++ .../Properties/AssemblyInfo.cs | 3 + src/BizHawk.Tests.Testroms.GB/TestUtils.cs | 75 + src/BizHawk.Tests.Testroms.GB/readme.md | 75 + .../res/download_from_ci.sh | 4 + .../run_tests_debug.sh | 2 + .../run_tests_release.sh | 2 + 25 files changed, 2965 insertions(+), 1 deletion(-) create mode 100644 src/BizHawk.Tests.Testroms.GB.GambatteSuite/BizHawk.Tests.Testroms.GB.GambatteSuite.csproj create mode 100644 src/BizHawk.Tests.Testroms.GB.GambatteSuite/GambatteSuite.Cases.cs create mode 100644 src/BizHawk.Tests.Testroms.GB.GambatteSuite/GambatteSuite.cs create mode 100644 src/BizHawk.Tests.Testroms.GB.GambatteSuite/Properties/AssemblyInfo.cs create mode 100644 src/BizHawk.Tests.Testroms.GB.GambatteSuite/readme.md create mode 100755 src/BizHawk.Tests.Testroms.GB.GambatteSuite/res/download_from_ci.sh create mode 100755 src/BizHawk.Tests.Testroms.GB.GambatteSuite/run_tests_debug.sh create mode 100755 src/BizHawk.Tests.Testroms.GB.GambatteSuite/run_tests_release.sh create mode 100755 src/BizHawk.Tests.Testroms.GB/.download_from_ci.sh create mode 100755 src/BizHawk.Tests.Testroms.GB/.run_tests_with_configuration.sh create mode 100644 src/BizHawk.Tests.Testroms.GB/BizHawk.Tests.Testroms.GB.csproj create mode 100644 src/BizHawk.Tests.Testroms.GB/DummyFrontend.cs create mode 100644 src/BizHawk.Tests.Testroms.GB/GB_GBC/AcidTestroms.cs create mode 100644 src/BizHawk.Tests.Testroms.GB/GB_GBC/BullyGB.cs create mode 100644 src/BizHawk.Tests.Testroms.GB/GB_GBC/GBHelper.cs create mode 100644 src/BizHawk.Tests.Testroms.GB/GB_GBC/MealybugTearoomTests.cs create mode 100644 src/BizHawk.Tests.Testroms.GB/GB_GBC/RTC3Test.cs create mode 100644 src/BizHawk.Tests.Testroms.GB/ImageUtils.cs create mode 100644 src/BizHawk.Tests.Testroms.GB/Properties/AssemblyInfo.cs create mode 100644 src/BizHawk.Tests.Testroms.GB/TestUtils.cs create mode 100644 src/BizHawk.Tests.Testroms.GB/readme.md create mode 100755 src/BizHawk.Tests.Testroms.GB/res/download_from_ci.sh create mode 100755 src/BizHawk.Tests.Testroms.GB/run_tests_debug.sh create mode 100755 src/BizHawk.Tests.Testroms.GB/run_tests_release.sh diff --git a/.gitignore b/.gitignore index 39a2dc7130..059c786e13 100644 --- a/.gitignore +++ b/.gitignore @@ -31,7 +31,8 @@ /src/BizHawk.Common/VersionInfo.gen.cs - +/src/BizHawk.Tests*/res/*_artifact +/src/BizHawk.Tests*/res/fw /Build/*.vshost.* diff --git a/src/BizHawk.Tests.Testroms.GB.GambatteSuite/BizHawk.Tests.Testroms.GB.GambatteSuite.csproj b/src/BizHawk.Tests.Testroms.GB.GambatteSuite/BizHawk.Tests.Testroms.GB.GambatteSuite.csproj new file mode 100644 index 0000000000..445ba22b97 --- /dev/null +++ b/src/BizHawk.Tests.Testroms.GB.GambatteSuite/BizHawk.Tests.Testroms.GB.GambatteSuite.csproj @@ -0,0 +1,14 @@ + + + net6.0 + + + + $(ProjectDir)../../test_output + + + + + + + diff --git a/src/BizHawk.Tests.Testroms.GB.GambatteSuite/GambatteSuite.Cases.cs b/src/BizHawk.Tests.Testroms.GB.GambatteSuite/GambatteSuite.Cases.cs new file mode 100644 index 0000000000..5688109d80 --- /dev/null +++ b/src/BizHawk.Tests.Testroms.GB.GambatteSuite/GambatteSuite.Cases.cs @@ -0,0 +1,1374 @@ +using System.Collections.Generic; + +using BizHawk.Emulation.Cores; + +using static BizHawk.Tests.Testroms.GB.GBHelper; + +namespace BizHawk.Tests.Testroms.GB.GambatteSuite +{ + public sealed partial class GambatteSuite + { + public readonly struct GambatteHexStrTestCase + { + public static readonly GambatteHexStrTestCase Dummy = new("missing_files", new(CoreNames.Gambatte, ConsoleVariant.DMG), string.Empty, string.Empty); + + public static readonly IReadOnlyCollection KnownFailures = new[] + { + "cgbpal_m3.cgbpal_m3end_1_cgb04c_out7 on CGB_C in GBHawk", + "cgbpal_m3.cgbpal_m3end_3_cgb04c_out0 on CGB_C in GBHawk", + "cgbpal_m3.cgbpal_m3end_scx2_1_cgb04c_out7 on CGB_C in GBHawk", + "cgbpal_m3.cgbpal_m3end_scx2_3_cgb04c_out0 on CGB_C in GBHawk", + "cgbpal_m3.cgbpal_m3end_scx5_1_cgb04c_out7 on CGB_C in GBHawk", + "cgbpal_m3.cgbpal_m3end_scx5_3_cgb04c_out0 on CGB_C in GBHawk", + "cgbpal_m3.cgbpal_m3end_scx5_ds_1_cgb04c_out7 on CGB_C in GBHawk", + "cgbpal_m3.cgbpal_m3end_scx5_ds_3_cgb04c_out0 on CGB_C in GBHawk", + "cgbpal_m3.cgbpal_m3start_ds_1_cgb04c_out1 on CGB_C in GBHawk", + "cgbpal_m3.cgbpal_read_m3start_ds_1_cgb04c_out00 on CGB_C in GBHawk", + "cgbpal_m3.cgbpal_read_m3start_lcdoffset1_1_cgb04c_out00 on CGB_C in GBHawk", + "cgbpal_m3.cgbpal_write_m3start_ds_1_cgb04c_out01 on CGB_C in GBHawk", + "cgbpal_m3.cgbpal_write_m3start_lcdoffset1_1_cgb04c_out01 on CGB_C in GBHawk", + "dma.hdma_late_enable_ds_lcdoffset1_2_cgb04c_out0 on CGB_C in GBHawk", + "dma.hdma_late_enable_lcdoffset3_1_cgb04c_out1 on CGB_C in GBHawk", + "dma.hdma_late_enable_lcdoffset3_2_cgb04c_out0 on CGB_C in GBHawk", + "dma.hdma_late_if_and_ie_halt_2_cgb04c_out02 on CGB_C in GBHawk", + "dma.hdma_late_m0halt_1_cgb04c_out00 on CGB_C in GBHawk", + "dma.hdma_late_m0halt_ds_1_cgb04c_out00 on CGB_C in GBHawk", + "dma.hdma_late_m0halt_ds_lcdoffset1_1_cgb04c_out00 on CGB_C in GBHawk", + "dma.hdma_late_m0halt_lcdoffset3_1_cgb04c_out00 on CGB_C in GBHawk", + "dma.hdma_late_m3halt_m2unhalt_inc_scx1_2_cgb04c_out02 on CGB_C in GBHawk", + "dma.hdma_late_m3halt_m2unhalt_inc_scx2_2_cgb04c_out02 on CGB_C in GBHawk", + "dma.hdma_late_m3halt_m2unhalt_scx1_2_cgb04c_outFF on CGB_C in GBHawk", + "dma.hdma_late_m3speedchange_hdma5_scx1_2_cgb04c_out80 on CGB_C in GBHawk", + "dma.hdma_late_m3speedchange_hdma5_scx1_ds_2_cgb04c_outFF on CGB_C in GBHawk", + "dma.hdma_late_m3speedchange_hdma5_scx2_2_cgb04c_out80 on CGB_C in GBHawk", + "dma.hdma_late_m3speedchange_inc_scx1_2_cgb04c_out02 on CGB_C in GBHawk", + "dma.hdma_late_m3speedchange_ly_scx1_4_cgb04c_out93 on CGB_C in GBHawk", + "dma.hdma_late_m3speedchange_read_hdmadst00_scx1_2_cgb04c_out9F on CGB_C in GBHawk", + "dma.hdma_late_m3speedchange_read_hdmadst00_scx1_ds_2_cgb04c_out9F on CGB_C in GBHawk", + "dma.hdma_late_m3speedchange_tima_scx1_ds_3_cgb04c_outF6 on CGB_C in GBHawk", + "dma.hdma_late_m3speedchange_tima_scx1_ds_4_cgb04c_outF7 on CGB_C in GBHawk", + "dma.hdma_late_speedchange_inc_scx1_ds_2_cgb04c_out02 on CGB_C in GBHawk", + "dma.hdma_m0halt_late_m3unhalt_scx1_2_cgb04c_out00 on CGB_C in GBHawk", + "dma.hdma_m0speedchange_late_m3wakeup_scx1_2_cgb04c_out00 on CGB_C in GBHawk", + "dma.hdma_m0speedchange_late_m3wakeup_scx2_1_cgb04c_outFF on CGB_C in GBHawk", + "dma.hdma_m0speedchange_late_m3wakeup_scx2_2_cgb04c_out00 on CGB_C in GBHawk", + "dma.hdma_transition_7fffhalt_inc_m3unhalt_cgb04c_out01 on CGB_C in GBHawk", + "dma.hdma_transition_ei_halt_late_unhalt_ldaaimm_hdma_scx1_1_cgb04c_out00 on CGB_C in GBHawk", + "dma.hdma_transition_ei_halt_late_unhalt_ldaaimm_hdma_scx1_2_cgb04c_out02 on CGB_C in GBHawk", + "dma.hdma_transition_ei_halt_late_unhalt_scx1_2_cgb04c_outFF on CGB_C in GBHawk", + "dma.hdma_transition_halt_hdmadst_unhalt_cgb04c_out01 on CGB_C in GBHawk", + "dma.hdma_transition_halt_late_unhalt_ldaaimm_hdma_scx1_1_cgb04c_out00 on CGB_C in GBHawk", + "dma.hdma_transition_halt_late_unhalt_ldaaimm_hdma_scx1_2_cgb04c_out02 on CGB_C in GBHawk", + "dma.hdma_transition_halt_late_unhalt_scx1_2_cgb04c_outFF on CGB_C in GBHawk", + "dma.hdma_transition_halt_m0unhalt_ldaaimm_scx1_cgb04c_out02 on CGB_C in GBHawk", + "dma.hdma_transition_speedchange_7fffstop_inc_cgb04c_out02 on CGB_C in GBHawk", + "dma.hdma_transition_speedchange_hdmalen00_hdma5_scx1_cgb04c_out80 on CGB_C in GBHawk", + "dma.hdma_transition_speedchange_hdmalen01_hdma5_scx1_cgb04c_out81 on CGB_C in GBHawk", + "dma.hdma_transition_speedchange_hdmalen01_hdmadst10_scx1_cgb04c_out00 on CGB_C in GBHawk", + "dma.hdma_transition_speedchange_hdmalen7f_hdma5_scx1_cgb04c_outFF on CGB_C in GBHawk", + "dma.hdma_transition_speedchange_hdmalen7f_hdmadst10_scx1_cgb04c_out00 on CGB_C in GBHawk", + "dma.hdma_transition_speedchange_ldaaimm_scx1_cgb04c_outFF on CGB_C in GBHawk", + "dma.hdma_transition_speedchange_ldaaimm_scx1_ds_cgb04c_out03 on CGB_C in GBHawk", + "dma.hdma_transition_speedchange_oamdma_cgb04c_out71 on CGB_C in GBHawk", + "enable_display.enable_display_ly0_m0irq_trigger_dmg08_cgb04c_out0 on DMG in GBHawk", + "enable_display.enable_display_ly0_oambusy_read_ds_1_cgb04c_out0 on CGB_C in GBHawk", + "enable_display.enable_display_ly0_wemaster_1_dmg08_cgb04c_out3 on CGB_C in GBHawk", + "enable_display.enable_display_ly0_wemaster_1_dmg08_cgb04c_out3 on DMG in GBHawk", + "enable_display.frame0_m0irq_count_scx2_1_dmg08_cgb04c_out90 on DMG in GBHawk", + "enable_display.frame0_m0irq_count_scx3_1_dmg08_cgb04c_out90 on DMG in GBHawk", + "enable_display.frame0_m2irq_count_1_dmg08_cgb04c_out98 on DMG in GBHawk", + "enable_display.frame0_m2stat_count_1_dmg08_cgb04c_out91 on CGB_C in GBHawk", + "enable_display.frame1_ly_count_2_dmg08_cgb04c_out9A on CGB_C in GBHawk", + "enable_display.frame1_m2irq_count_2_dmg08_cgb04c_out91 on DMG in GBHawk", + "enable_display.frame1_m2stat_count_1_dmg08_cgb04c_out91 on CGB_C in GBHawk", + "enable_display.ly0_late_cgbpr_1_cgb04c_out55 on CGB_C in GBHawk", + "enable_display.ly0_late_cgbpr_ds_1_cgb04c_out55 on CGB_C in GBHawk", + "enable_display.ly0_late_cgbpw_1_cgb04c_outAA on CGB_C in GBHawk", + "enable_display.ly0_late_cgbpw_ds_1_cgb04c_outAA on CGB_C in GBHawk", + "enable_display.ly0_late_oamw_ds_2_cgb04c_out55 on CGB_C in GBHawk", + "enable_display.ly0_late_scx7_m3stat_scx0_2_dmg08_out87_cgb04c_out84 on DMG in GBHawk", + "enable_display.ly0_late_scx7_m3stat_scx1_1_dmg08_cgb04c_out87 on CGB_C in GBHawk", + "enable_display.ly0_late_scx7_m3stat_scx1_1_dmg08_cgb04c_out87 on DMG in GBHawk", + "enable_display.ly0_late_scx7_m3stat_scx3_1_dmg08_cgb04c_out87 on CGB_C in GBHawk", + "enable_display.ly0_late_scx7_m3stat_scx3_1_dmg08_cgb04c_out87 on DMG in GBHawk", + "enable_display.ly0_late_vramr_2_dmg08_outFF_cgb04c_out55 on CGB_C in GBHawk", + "enable_display.ly0_late_vramw_2_dmg08_out55_cgb04c_outAA on CGB_C in GBHawk", + "enable_display.ly0_m0irq_scx0_1_dmg08_cgb04c_outE0 on DMG in GBHawk", + "enable_display.ly0_m0irq_scx1_1_dmg08_cgb04c_outE0 on DMG in GBHawk", + "irq_precedence.late_m0irq_retrigger_scx1_1_dmg08_cgb04c_outE2 on CGB_C in GBHawk", + "irq_precedence.late_m0irq_retrigger_scx1_1_dmg08_cgb04c_outE2 on DMG in GBHawk", + "lcd_offset.offset1_lyc8fint_m1stat_ds_1_cgb04c_outC0 on CGB_C in GBHawk", + "lcd_offset.offset1_lyc98int_ly_count_ds_2_cgb04c_out9A on CGB_C in GBHawk", + "lcd_offset.offset1_lyc99int_m0irq_count_scx1_ds_2_cgb04c_out90 on CGB_C in GBHawk", + "lcd_offset.offset1_lyc99int_m0stat_count_scx1_ds_2_cgb04c_out90 on CGB_C in GBHawk", + "lcd_offset.offset1_lyc99int_m0stat_count_scx3_1_cgb04c_out90 on CGB_C in GBHawk", + "lcd_offset.offset1_lyc99int_m2irq_count_1_cgb04c_out98 on CGB_C in GBHawk", + "lcd_offset.offset1_lyc99int_m2stat_count_ds_2_cgb04c_out90 on CGB_C in GBHawk", + "lcd_offset.offset1_lyc99int_m3stat_count_ds_2_cgb04c_out90 on CGB_C in GBHawk", + "lcd_offset.offset2_lyc98int_ly_count_2_cgb04c_out9A on CGB_C in GBHawk", + "lcd_offset.offset2_lyc99int_m0stat_count_scx2_1_cgb04c_out90 on CGB_C in GBHawk", + "lcd_offset.offset2_lyc99int_m2irq_count_1_cgb04c_out98 on CGB_C in GBHawk", + "lcd_offset.offset3_lyc8fint_m1irq_1_cgb04c_outE0 on CGB_C in GBHawk", + "lcd_offset.offset3_lyc8fint_m1stat_1_cgb04c_outC0 on CGB_C in GBHawk", + "lcd_offset.offset3_lyc98int_ly_count_2_cgb04c_out9A on CGB_C in GBHawk", + "lcd_offset.offset3_lyc99int_m0stat_count_scx1_1_cgb04c_out90 on CGB_C in GBHawk", + "lcd_offset.offset3_lyc99int_m2irq_count_1_cgb04c_out98 on CGB_C in GBHawk", + "lcd_offset.offset3_lyc99int_m2irq_count_2_cgb04c_out91 on CGB_C in GBHawk", + "lcdirq_precedence.lcdirqprecedence_lycirq_ly90_lcdstat68_dmg08_cgb04c_out1 on CGB_C in GBHawk", + "lcdirq_precedence.m2irq_ly00_lcdstat30_dmg08_cgb04c_out0 on DMG in GBHawk", + "ly0.lycint152_ly0stat_2_dmg08_cgb04c_outC0 on CGB_C in GBHawk", + "ly0.lycint152_ly153_3_dmg08_cgb04c_out00 on CGB_C in GBHawk", + "ly0.lycint152_lyc0flag_4_dmg08_cgb04c_outC0 on CGB_C in GBHawk", + "ly0.lycint152_lyc0irq_late_retrigger_2_dmg08_cgb04c_outE0 on CGB_C in GBHawk", + "ly0.lycint152_lyc153flag_3_dmg08_cgb04c_outC1 on CGB_C in GBHawk", + "ly0.lycint152_lyc153irq_late_retrigger_2_dmg08_cgb04c_outE0 on CGB_C in GBHawk", + "lycEnable.ff41_disable_2_dmg08_out0_cgb04c_out2 on CGB_C in GBHawk", + "lycEnable.ff41_disable_2_dmg08_out0_cgb04c_out2 on DMG in GBHawk", + "lycEnable.ff45_disable_ds_1_cgb04c_out1 on CGB_C in GBHawk", + "lycEnable.ff45_enable_weirdpoint_2_dmg08_out3_cgb04c_out1 on CGB_C in GBHawk", + "lycEnable.ff45_enable_weirdpoint_3_dmg08_out1_cgb04c_out3 on DMG in GBHawk", + "lycEnable.ff45_enable_weirdpoint_ds_2_cgb04c_out1 on CGB_C in GBHawk", + "lycEnable.ff45_enable_weirdpoint_ds_3_cgb04c_out1 on CGB_C in GBHawk", + "lycEnable.ff45_enable_weirdpoint_ds_lcdoffset1_2_cgb04c_out0 on CGB_C in GBHawk", + "lycEnable.ff45_enable_weirdpoint_ds_lcdoffset1_3_cgb04c_out0 on CGB_C in GBHawk", + "lycEnable.ff45_enable_weirdpoint_lcdoffset1_2_cgb04c_out0 on CGB_C in GBHawk", + "lycEnable.late_ff41_enable_after_m2int_disable_dmg08_cgb04c_out2 on CGB_C in GBHawk", + "lycEnable.late_ff41_enable_after_m2int_disable_dmg08_cgb04c_out2 on DMG in GBHawk", + "lycEnable.late_ff41_enable_after_m2int_dmg08_cgb04c_out2 on CGB_C in GBHawk", + "lycEnable.late_ff41_enable_after_m2int_dmg08_cgb04c_out2 on DMG in GBHawk", + "lycEnable.late_ff41_enable_ds_1_cgb04c_out3 on CGB_C in GBHawk", + "lycEnable.late_ff41_enable_ds_lcdoffset1_1_cgb04c_out2 on CGB_C in GBHawk", + "lycEnable.late_ff41_enable_lcdoffset1_1_cgb04c_out2 on CGB_C in GBHawk", + "lycEnable.late_ff45_enable_after_m2int_dmg08_cgb04c_out2 on CGB_C in GBHawk", + "lycEnable.late_ff45_enable_after_m2int_dmg08_cgb04c_out2 on DMG in GBHawk", + "lycEnable.late_ff45_enable_ds_1_cgb04c_out3 on CGB_C in GBHawk", + "lycEnable.late_ff45_enable_ds_lcdoffset1_1_cgb04c_out2 on CGB_C in GBHawk", + "lycEnable.late_ff45_enable_lcdoffset1_1_cgb04c_out2 on CGB_C in GBHawk", + "lycEnable.lcdoff_lycirqen_1_dmg08_cgb04c_outE2 on CGB_C in GBHawk", + "lycEnable.lcdoff_lycirqen_1_dmg08_cgb04c_outE2 on DMG in GBHawk", + "lycEnable.lcdoff_lycirqen_4_dmg08_outE2_cgb04c_outE0 on DMG in GBHawk", + "lycEnable.lyc_ff45_disable2_ds_1_cgb04c_out1 on CGB_C in GBHawk", + "lycEnable.lyc_ff45_trigger_delay_ds_1_cgb04c_out0 on CGB_C in GBHawk", // completely blank + "lycEnable.lyc_ff45_trigger_delay_ds_2_cgb04c_out2 on CGB_C in GBHawk", // completely blank + "lycEnable.lyc0_ff41_disable_2_dmg08_cgb04c_outE2 on CGB_C in GBHawk", + "lycEnable.lyc0_ff45_disable_ds_1_cgb04c_outE0 on CGB_C in GBHawk", + "lycEnable.lyc0_m1disable_2_dmg08_outE2_cgb04c_outE0 on CGB_C in GBHawk", + "lycEnable.lyc0_m1disable_2_dmg08_outE2_cgb04c_outE0 on DMG in GBHawk", + "lycEnable.lyc153_late_enable_m1disable_2_dmg08_outE2_cgb04c_outE0 on CGB_C in GBHawk", + "lycEnable.lyc153_late_enable_m1disable_2_dmg08_outE2_cgb04c_outE0 on DMG in GBHawk", + "lycEnable.lyc153_late_ff41_enable_ds_1_cgb04c_outE2 on CGB_C in GBHawk", + "lycEnable.lyc153_late_ff41_enable_ds_lcdoffset1_1_cgb04c_outE2 on CGB_C in GBHawk", + "lycEnable.lyc153_late_ff41_enable_lcdoffset1_1_cgb04c_outE2 on CGB_C in GBHawk", + "lycEnable.lyc153_late_ff45_enable_2_dmg08_outE2_cgb04c_outE0 on CGB_C in GBHawk", + "lycEnable.lyc153_late_ff45_enable_3_dmg08_outE0_cgb04c_outE2 on DMG in GBHawk", + "lycEnable.lyc153_late_ff45_enable_ds_2_cgb04c_outE0 on CGB_C in GBHawk", + "lycEnable.lyc153_late_ff45_enable_ds_3_cgb04c_outE0 on CGB_C in GBHawk", + "lycEnable.lyc153_late_ff45_enable_ds_4_cgb04c_outE2 on CGB_C in GBHawk", + "lycEnable.lyc153_late_ff45_enable_ds_5_cgb04c_outE2 on CGB_C in GBHawk", + "lycEnable.lyc153_late_ff45_enable_ds_lcdoffset1_1_cgb04c_outE2 on CGB_C in GBHawk", + "lycEnable.lyc153_late_ff45_enable_lcdoffset1_1_cgb04c_outE2 on CGB_C in GBHawk", + "lycEnable.lyc153_late_m1disable_2_dmg08_outE2_cgb04c_outE0 on CGB_C in GBHawk", + "lycEnable.lyc153_late_m1disable_2_dmg08_outE2_cgb04c_outE0 on DMG in GBHawk", + "lycEnable.lycwirq_trigger_ly00_stat50_3_dmg08_cgb04c_outE2 on DMG in GBHawk", + "lycEnable.lycwirq_trigger_ly00_stat50_ds_1_cgb04c_outE0 on CGB_C in GBHawk", + "lycEnable.lycwirq_trigger_ly00_stat50_ds_lcdoffset1_1_cgb04c_outE0 on CGB_C in GBHawk", + "lycEnable.lycwirq_trigger_ly00_stat50_lcdoffset1_1_cgb04c_outE0 on CGB_C in GBHawk", + "lycint_lycflag.lycint_lycflag_4_dmg08_cgb04c_out0 on CGB_C in GBHawk", + "m0enable.disable_2_dmg08_out0_cgb04c_out2 on CGB_C in GBHawk", + "m0enable.disable_scx3_2_dmg08_out0_cgb04c_out2 on DMG in GBHawk", + "m0enable.disable_scx4_2_dmg08_out0_cgb04c_out2 on CGB_C in GBHawk", + "m0enable.disable_scx5_ds_2_cgb04c_out3 on CGB_C in GBHawk", + "m0enable.disable_scx7_2_dmg08_out0_cgb04c_out2 on DMG in GBHawk", + "m0enable.enable_wxA6_2x_spxA7_1_dmg08_cgb04c_out2 on CGB_C in GBHawk", + "m0enable.enable_wxA6_2x_spxA7_1_dmg08_cgb04c_out2 on DMG in GBHawk", + "m0enable.enable_wxA6_2x_spxA7_2_dmg08_cgb04c_out2 on CGB_C in GBHawk", + "m0enable.enable_wxA6_2x_spxA7_2_dmg08_cgb04c_out2 on DMG in GBHawk", + "m0enable.enable_wxA6_2x_spxA7_ds_1_cgb04c_out2 on CGB_C in GBHawk", + "m0enable.enable_wxA6_2x_spxA7_ds_2_cgb04c_out2 on CGB_C in GBHawk", + "m0enable.enable_wxA6_2x_spxA7_ds_3_cgb04c_out2 on CGB_C in GBHawk", + "m0enable.enable_wxA6_2x_spxA7_ds_4_cgb04c_out2 on CGB_C in GBHawk", + "m0enable.late_enable_2_dmg08_out2_cgb04c_out0 on CGB_C in GBHawk", + "m0enable.late_enable_ds_2_cgb04c_out1 on CGB_C in GBHawk", + "m0enable.late_enable_ds_lcdoffset1_2_cgb04c_out0 on CGB_C in GBHawk", + "m0enable.lycdisable_ff41_2_dmg08_out2_cgb04c_out0 on DMG in GBHawk", + "m0enable.lycdisable_ff41_scx3_2_dmg08_out2_cgb04c_out0 on DMG in GBHawk", + "m0enable.lycdisable_ff45_3_dmg08_out2_cgb04c_out0 on DMG in GBHawk", + "m0enable.lycdisable_ff45_scx1_ds_1_cgb04c_out2 on CGB_C in GBHawk", + "m0enable.reenable_1_dmg08_cgb04c_out2 on DMG in GBHawk", + "m0enable.reenable_2_dmg08_cgb04c_out2 on DMG in GBHawk", + "m1.ly143_late_m0enable_ds_lcdoffset1_2_cgb04c_out1 on CGB_C in GBHawk", + "m1.ly143_late_m0enable_lcdoffset1_1_cgb04c_out3 on CGB_C in GBHawk", + "m1.ly143_late_m2enable_ds_lcdoffset1_2_cgb04c_out1 on CGB_C in GBHawk", + "m1.lyc143_late_m0enable_lycdisable_2_dmg08_cgb04c_out1 on DMG in GBHawk", + "m1.lyc143_late_m2enable_lycdisable_1_dmg08_cgb04c_out3 on DMG in GBHawk", + "m1.lyc143_late_m2enable_lycdisable_2_dmg08_cgb04c_out1 on DMG in GBHawk", + "m1.m1irq_enable_after_lyc144_2_dmg08_out1_cgb04c_out3 on DMG in GBHawk", + "m1.m1irq_late_enable_ds_lcdoffset1_2_cgb04c_out0 on CGB_C in GBHawk", + "m1.m1irq_late_enable_lcdoffset1_1_cgb04c_out2 on CGB_C in GBHawk", + "m1.m1irq_m0disable_2_dmg08_out3_cgb04c_out1 on CGB_C in GBHawk", + "m1.m1irq_m0disable_2_dmg08_out3_cgb04c_out1 on DMG in GBHawk", + "m1.m1irq_m0enable_2_dmg08_cgb04c_out3 on CGB_C in GBHawk", + "m1.m1irq_m2disable_lycdisable_2_dmg08_out3_cgb04c_out1 on DMG in GBHawk", + "m1.m1irq_m2enable_lyc_2_dmg08_out1_cgb04c_out3 on DMG in GBHawk", + "m2enable.late_enable_1_dmg08_cgb04c_out2 on CGB_C in GBHawk", + "m2enable.late_enable_after_lycint_2_dmg08_out0_cgb04c_out2 on CGB_C in GBHawk", + "m2enable.late_enable_after_lycint_2_dmg08_out0_cgb04c_out2 on DMG in GBHawk", + "m2enable.late_enable_after_lycint_disable_1_dmg08_cgb04c_out2 on DMG in GBHawk", + "m2enable.late_enable_after_lycint_disable_2_dmg08_out0_cgb04c_out2 on CGB_C in GBHawk", + "m2enable.late_enable_after_lycint_disable_2_dmg08_out0_cgb04c_out2 on DMG in GBHawk", + "m2enable.late_enable_ds_1_cgb04c_out2 on CGB_C in GBHawk", + "m2enable.late_enable_ds_lcdoffset1_1_cgb04c_out2 on CGB_C in GBHawk", + "m2enable.late_enable_lcdoffset2_1_cgb04c_out2 on CGB_C in GBHawk", + "m2enable.late_enable_lcdoffset3_1_cgb04c_out2 on CGB_C in GBHawk", + "m2enable.late_enable_ly0_ds_1_cgb04c_out2 on CGB_C in GBHawk", + "m2enable.late_enable_m0disable_1_dmg08_cgb04c_out2 on DMG in GBHawk", + "m2enable.late_enable_m0disable_2_dmg08_out0_cgb04c_out2 on CGB_C in GBHawk", + "m2enable.late_enable_m0disable_2_dmg08_out0_cgb04c_out2 on DMG in GBHawk", + "m2enable.late_enable_m0disable_ds_1_cgb04c_out2 on CGB_C in GBHawk", + "m2enable.late_enable_m1disable_ly0_1_dmg08_cgb04c_out2 on DMG in GBHawk", + "m2enable.late_enable_m1disable_ly0_ds_1_cgb04c_out2 on CGB_C in GBHawk", + "m2enable.late_m1disable_ly0_1_dmg08_cgb04c_out2 on DMG in GBHawk", + "m2enable.late_m1disable_ly0_3_dmg08_cgb04c_out0 on DMG in GBHawk", + "m2enable.lyc0_late_m2enable_lycdisable_1_dmg08_cgb04c_out2 on DMG in GBHawk", + "m2enable.lyc0_late_m2enable_lycdisable_2_dmg08_out2_cgb04c_out0 on DMG in GBHawk", + "m2enable.lyc1_late_m2enable_lycdisable_1_dmg08_out0_cgb04c_out2 on CGB_C in GBHawk", + "m2enable.lyc1_late_m2enable_lycdisable_1_dmg08_out0_cgb04c_out2 on DMG in GBHawk", + "m2enable.lyc1_late_m2enable_lycdisable_ds_3_cgb04c_out2 on CGB_C in GBHawk", + "m2enable.lyc1_m2irq_late_lyc255_ds_1_cgb04c_out2 on CGB_C in GBHawk", + "m2enable.lyc1_m2irq_late_lycdisable_1_dmg08_cgb04c_out2 on DMG in GBHawk", + "m2enable.m2_late_m0disable_1_dmg08_cgb04c_out2 on DMG in GBHawk", + "m2int_m0irq.m2int_m0irq_scx3_ifw_ds_2_cgb04c_out0 on CGB_C in GBHawk", + "m2int_m3stat.scx.late_scx4_ds_1_cgb04c_out3 on CGB_C in GBHawk", + "miscmstatirq.lycflag_statwirq_2_dmg08_out2 on DMG in GBHawk", + "miscmstatirq.lycstatwirq_trigger_00_00_dmg08_out2_cgb04c_out0 on DMG in GBHawk", + "miscmstatirq.lycstatwirq_trigger_00_bf_dmg08_out2_cgb04c_out0 on DMG in GBHawk", + "miscmstatirq.lycstatwirq_trigger_bf_00_dmg08_out2_cgb04c_out0 on DMG in GBHawk", + "miscmstatirq.lycstatwirq_trigger_bf_bf_dmg08_out2_cgb04c_out0 on DMG in GBHawk", + "miscmstatirq.lycstatwirq_trigger_ly00_10_50_2_dmg08_cgb04c_outE2 on DMG in GBHawk", + "miscmstatirq.lycstatwirq_trigger_ly00_10_50_ds_lcdoffset1_2_cgb04c_outE2 on CGB_C in GBHawk", + "miscmstatirq.lycstatwirq_trigger_ly94_00_50_dmg08_cgb04c_outE2 on DMG in GBHawk", + "miscmstatirq.lycstatwirq_trigger_m0_late_ly44_lyc44_08_40_4_dmg08_cgb04c_outE0 on DMG in GBHawk", + "miscmstatirq.lycstatwirq_trigger_m0_late_ly44_lyc44_08_40_ds_3_cgb04c_outE2 on CGB_C in GBHawk", + "miscmstatirq.lycstatwirq_trigger_m0_ly44_lyc44_00_40_dmg08_cgb04c_outE2 on DMG in GBHawk", + "miscmstatirq.lycstatwirq_trigger_m0_ly44_lyc44_00_48_dmg08_cgb04c_outE2 on DMG in GBHawk", + "miscmstatirq.lycstatwirq_trigger_m0_ly44_lyc44_b7_40_dmg08_cgb04c_outE2 on DMG in GBHawk", + "miscmstatirq.lycstatwirq_trigger_m0_ly44_lyc44_b7_f7_dmg08_cgb04c_outE2 on DMG in GBHawk", + "miscmstatirq.m0statwirq_scx3_1_dmg08_out0 on DMG in GBHawk", + "miscmstatirq.m0statwirq_trigger_00_00_dmg08_out2_cgb04c_out0 on DMG in GBHawk", + "miscmstatirq.m0statwirq_trigger_00_08_dmg08_out2_cgb04c_out2 on DMG in GBHawk", + "miscmstatirq.m0statwirq_trigger_00_f7_dmg08_out2_cgb04c_out0 on DMG in GBHawk", + "miscmstatirq.m0statwirq_trigger_00_ff_dmg08_out2_cgb04c_out2 on DMG in GBHawk", + "miscmstatirq.m0statwirq_trigger_f7_00_dmg08_out2_cgb04c_out0 on DMG in GBHawk", + "miscmstatirq.m0statwirq_trigger_f7_08_dmg08_out2_cgb04c_out2 on DMG in GBHawk", + "miscmstatirq.m0statwirq_trigger_f7_f7_dmg08_out2_cgb04c_out0 on DMG in GBHawk", + "miscmstatirq.m0statwirq_trigger_f7_ff_dmg08_out2_cgb04c_out2 on DMG in GBHawk", + "miscmstatirq.m0statwirq_trigger_ly44_lyc44_00_08_dmg08_cgb04c_outE2 on DMG in GBHawk", + "miscmstatirq.m1statwirq_3_dmg08_out2 on DMG in GBHawk", + "miscmstatirq.m1statwirq_trigger_00_00_dmg08_out2_cgb04c_out0 on DMG in GBHawk", + "miscmstatirq.m1statwirq_trigger_00_10_dmg08_out2_cgb04c_out2 on DMG in GBHawk", + "miscmstatirq.m1statwirq_trigger_00_ef_dmg08_out2_cgb04c_out0 on DMG in GBHawk", + "miscmstatirq.m1statwirq_trigger_00_ff_dmg08_out2_cgb04c_out2 on DMG in GBHawk", + "miscmstatirq.m1statwirq_trigger_ef_00_dmg08_out2_cgb04c_out0 on DMG in GBHawk", + "miscmstatirq.m1statwirq_trigger_ef_10_dmg08_out2_cgb04c_out2 on DMG in GBHawk", + "miscmstatirq.m1statwirq_trigger_ef_ef_dmg08_out2_cgb04c_out0 on DMG in GBHawk", + "miscmstatirq.m1statwirq_trigger_ef_ff_dmg08_out2_cgb04c_out2 on DMG in GBHawk", + "miscmstatirq.m1statwirq_trigger_ly94_lyc94_00_10_dmg08_cgb04c_outE2 on DMG in GBHawk", + "miscmstatirq.m1statwirq_trigger_ly94_lyc94_00_50_dmg08_cgb04c_outE2 on DMG in GBHawk", + "miscmstatirq.m1statwirq_trigger_ly94_lyc94_40_50_2_dmg08_outE0_cgb04c_outE2 on DMG in GBHawk", + "oam_access.10spritesprline_postread_2_dmg08_cgb04c_out0 on CGB_C in GBHawk", + "oam_access.10spritesprline_postread_2_dmg08_cgb04c_out0 on DMG in GBHawk", + "oam_access.midwrite_2_dmg08_out1_cgb04c_out0 on CGB_C in GBHawk", + "oam_access.postread_ds_2_cgb04c_out0 on CGB_C in GBHawk", + "oam_access.postread_scx3_2_dmg08_xout1_cgb04c_out0 on CGB_C in GBHawk", + "oam_access.postwrite_2_scx3_dmg08_cgb04c_out1 on CGB_C in GBHawk", + "oam_access.postwrite_2_scx3_dmg08_cgb04c_out1 on DMG in GBHawk", + "oam_access.postwrite_ds_2_cgb04c_out1 on CGB_C in GBHawk", + "oam_access.preread_ds_1_cgb04c_out0 on CGB_C in GBHawk", + "oam_access.preread_ds_lcdoffset1_1_cgb04c_out0 on CGB_C in GBHawk", + "oam_access.preread_lcdoffset1_1_cgb04c_out0 on CGB_C in GBHawk", + "oam_access.prewrite_ds_1_cgb04c_out1 on CGB_C in GBHawk", + "oam_access.prewrite_ds_lcdoffset1_1_cgb04c_out1 on CGB_C in GBHawk", + "oam_access.prewrite_lcdoffset1_1_cgb04c_out1 on CGB_C in GBHawk", + "oamdma.late_sp00x_2_dmg08_cgb04c_out3 on CGB_C in GBHawk", + "oamdma.late_sp00x_2_dmg08_cgb04c_out3 on DMG in GBHawk", + "oamdma.late_sp00x_ds_2_cgb04c_out3 on CGB_C in GBHawk", + "oamdma.late_sp00y_2_dmg08_cgb04c_out0 on CGB_C in GBHawk", + "oamdma.late_sp00y_2_dmg08_cgb04c_out0 on DMG in GBHawk", + "oamdma.late_sp00y_ds_2_cgb04c_out0 on CGB_C in GBHawk", + "oamdma.late_sp01x_2_dmg08_cgb04c_out3 on CGB_C in GBHawk", + "oamdma.late_sp01x_2_dmg08_cgb04c_out3 on DMG in GBHawk", + "oamdma.late_sp01x_ds_2_cgb04c_out3 on CGB_C in GBHawk", + "oamdma.late_sp01y_ds_2_cgb04c_out0 on CGB_C in GBHawk", + "oamdma.late_sp02x_2_dmg08_cgb04c_out3 on CGB_C in GBHawk", + "oamdma.late_sp02x_2_dmg08_cgb04c_out3 on DMG in GBHawk", + "oamdma.late_sp02y_2_dmg08_cgb04c_out0 on CGB_C in GBHawk", + "oamdma.late_sp02y_2_dmg08_cgb04c_out0 on DMG in GBHawk", + "oamdma.late_sp39x_1_dmg08_cgb04c_out0 on CGB_C in GBHawk", + "oamdma.late_sp39x_1_dmg08_cgb04c_out0 on DMG in GBHawk", + "oamdma.late_sp39x_3_cgb04c_out0 on CGB_C in GBHawk", + "oamdma.late_sp39x_ds_1_cgb04c_out0 on CGB_C in GBHawk", + "oamdma.late_sp39y_1_dmg08_cgb04c_out3 on CGB_C in GBHawk", + "oamdma.late_sp39y_1_dmg08_cgb04c_out3 on DMG in GBHawk", + "oamdma.late_sp39y_ds_1_cgb04c_out3 on CGB_C in GBHawk", + "oamdma.oamdma_late_halt_stat_2_dmg08_cgb04c_out3 on CGB_C in GBHawk", + "oamdma.oamdma_late_halt_stat_2_dmg08_cgb04c_out3 on DMG in GBHawk", + "oamdma.oamdma_late_speedchange_stat_2_cgb04c_out3 on CGB_C in GBHawk", + "oamdma.oamdma_src0000_busycallAFFF_dmg08_cgb04c_outFF8F on CGB_C in GBHawk", + "oamdma.oamdma_src0000_busycallAFFF_dmg08_cgb04c_outFF8F on DMG in GBHawk", + "oamdma.oamdma_src0000_busyint0002_dmg08_cgb04c_outFF941234 on CGB_C in GBHawk", + "oamdma.oamdma_src0000_busyint0002_dmg08_cgb04c_outFF941234 on DMG in GBHawk", + "oamdma.oamdma_src0000_busypop7FFF_dmg08_cgb04c_out657665AA on CGB_C in GBHawk", + "oamdma.oamdma_src0000_busypop7FFF_dmg08_cgb04c_out657665AA on DMG in GBHawk", + "oamdma.oamdma_src0000_busypop9FFF_2_dmg08_cgb04c_out65765576 on CGB_C in GBHawk", + "oamdma.oamdma_src0000_busypop9FFF_2_dmg08_cgb04c_out65765576 on DMG in GBHawk", + "oamdma.oamdma_src0000_busypop9FFF_dmg08_cgb04c_out65765576 on CGB_C in GBHawk", + "oamdma.oamdma_src0000_busypop9FFF_dmg08_cgb04c_out65765576 on DMG in GBHawk", + "oamdma.oamdma_src0000_busypopBFFF_2_dmg08_out65766576_cgb04c_out657665AA on CGB_C in GBHawk", + "oamdma.oamdma_src0000_busypopBFFF_dmg08_out65766576_cgb04c_out657665AA on CGB_C in GBHawk", + "oamdma.oamdma_src0000_busypopDFFF_dmg08_out65766576_cgb04c_out657655AA on CGB_C in GBHawk", + "oamdma.oamdma_src0000_busypopDFFF_dmg08_out65766576_cgb04c_out657655AA on DMG in GBHawk", + "oamdma.oamdma_src0000_busypopEFFF_dmg08_out65766576_cgb04c_out657655AA on CGB_C in GBHawk", + "oamdma.oamdma_src0000_busypopEFFF_dmg08_out65766576_cgb04c_out657655AA on DMG in GBHawk", + "oamdma.oamdma_src0000_busypopFDFF_dmg08_out657665FF_cgb04c_out657655FF on CGB_C in GBHawk", + "oamdma.oamdma_src0000_busypopFDFF_dmg08_out657665FF_cgb04c_out657655FF on DMG in GBHawk", + "oamdma.oamdma_src0000_busypopFE9F_dmg08_cgb04c_out6576FFFF on CGB_C in GBHawk", + "oamdma.oamdma_src0000_busypopFE9F_dmg08_cgb04c_out6576FFFF on DMG in GBHawk", + "oamdma.oamdma_src0000_busypopFEFF_dmg08_cgb04c_out6576FFEF on CGB_C in GBHawk", + "oamdma.oamdma_src0000_busypopFEFF_dmg08_cgb04c_out6576FFEF on DMG in GBHawk", + "oamdma.oamdma_src0000_busypush0001_dmg08_cgb04c_out5576AA34 on CGB_C in GBHawk", + "oamdma.oamdma_src0000_busypush0001_dmg08_cgb04c_out5576AA34 on DMG in GBHawk", + "oamdma.oamdma_src0000_busypush8001_dmg08_cgb04c_out65AA1255 on CGB_C in GBHawk", + "oamdma.oamdma_src0000_busypush8001_dmg08_cgb04c_out65AA1255 on DMG in GBHawk", + "oamdma.oamdma_src0000_busypushA001_2_dmg08_cgb04c_out5576AAFF on CGB_C in GBHawk", + "oamdma.oamdma_src0000_busypushA001_2_dmg08_cgb04c_out5576AAFF on DMG in GBHawk", + "oamdma.oamdma_src0000_busypushA001_dmg08_cgb04c_out5576AA34 on CGB_C in GBHawk", + "oamdma.oamdma_src0000_busypushA001_dmg08_cgb04c_out5576AA34 on DMG in GBHawk", + "oamdma.oamdma_src0000_busypushC001_2_dmg08_out55AAFF34_cgb04c_out65AAFF55 on CGB_C in GBHawk", + "oamdma.oamdma_src0000_busypushC001_2_dmg08_out55AAFF34_cgb04c_out65AAFF55 on DMG in GBHawk", + "oamdma.oamdma_src0000_busypushC001_dmg08_out55AA1234_cgb04c_out65AA1255 on CGB_C in GBHawk", + "oamdma.oamdma_src0000_busypushC001_dmg08_out55AA1234_cgb04c_out65AA1255 on DMG in GBHawk", + "oamdma.oamdma_src0000_busypushE001_dmg08_out55AA1234_cgb04c_out6576AA55 on CGB_C in GBHawk", + "oamdma.oamdma_src0000_busypushE001_dmg08_out55AA1234_cgb04c_out6576AA55 on DMG in GBHawk", + "oamdma.oamdma_src0000_busypushF001_dmg08_out55AA1234_cgb04c_out6576AA55 on CGB_C in GBHawk", + "oamdma.oamdma_src0000_busypushF001_dmg08_out55AA1234_cgb04c_out6576AA55 on DMG in GBHawk", + "oamdma.oamdma_src0000_busypushFE01_dmg08_out65AA1298_cgb04c_out6576AA98 on CGB_C in GBHawk", + "oamdma.oamdma_src0000_busypushFE01_dmg08_out65AA1298_cgb04c_out6576AA98 on DMG in GBHawk", + "oamdma.oamdma_src0000_busypushFEA1_dmg08_out65768700_cgb04c_out65768734 on CGB_C in GBHawk", + "oamdma.oamdma_src0000_busypushFF01_dmg08_out657600DF_cgb04c_out657612DF on CGB_C in GBHawk", + "oamdma.oamdma_src0000_busyread8000_dmg08_cgb04c_out1 on CGB_C in GBHawk", + "oamdma.oamdma_src0000_busyread8000_dmg08_cgb04c_out1 on DMG in GBHawk", + "oamdma.oamdma_src0000_busyreadA000_1_dmg08_cgb04c_out5 on CGB_C in GBHawk", + "oamdma.oamdma_src0000_busyreadC000_1_dmg08_out5_cgb04c_out1 on CGB_C in GBHawk", + "oamdma.oamdma_src0000_busyrst0002_dmg08_cgb04c_outFF8DFA9E on CGB_C in GBHawk", + "oamdma.oamdma_src0000_busyrst0002_dmg08_cgb04c_outFF8DFA9E on DMG in GBHawk", + "oamdma.oamdma_src7F00_busypop7FFF_dmg08_cgb04c_out657665AA on CGB_C in GBHawk", + "oamdma.oamdma_src7F00_busypop7FFF_dmg08_cgb04c_out657665AA on DMG in GBHawk", + "oamdma.oamdma_src7F00_busypop9FFF_2_dmg08_cgb04c_out65765576 on CGB_C in GBHawk", + "oamdma.oamdma_src7F00_busypop9FFF_2_dmg08_cgb04c_out65765576 on DMG in GBHawk", + "oamdma.oamdma_src7F00_busypop9FFF_dmg08_cgb04c_out65765576 on CGB_C in GBHawk", + "oamdma.oamdma_src7F00_busypop9FFF_dmg08_cgb04c_out65765576 on DMG in GBHawk", + "oamdma.oamdma_src7F00_busypopBFFF_2_dmg08_out65766576_cgb04c_out657665AA on CGB_C in GBHawk", + "oamdma.oamdma_src7F00_busypopBFFF_dmg08_out65766576_cgb04c_out657665AA on CGB_C in GBHawk", + "oamdma.oamdma_src7F00_busypopDFFF_dmg08_out65766576_cgb04c_out657655AA on CGB_C in GBHawk", + "oamdma.oamdma_src7F00_busypopDFFF_dmg08_out65766576_cgb04c_out657655AA on DMG in GBHawk", + "oamdma.oamdma_src7F00_busypopEFFF_dmg08_out65766576_cgb04c_out657655AA on CGB_C in GBHawk", + "oamdma.oamdma_src7F00_busypopEFFF_dmg08_out65766576_cgb04c_out657655AA on DMG in GBHawk", + "oamdma.oamdma_src7F00_busypopFDFF_dmg08_out657665FF_cgb04c_out657655FF on DMG in GBHawk", + "oamdma.oamdma_src7F00_busypopFE9F_dmg08_cgb04c_out6576FFFF on CGB_C in GBHawk", + "oamdma.oamdma_src7F00_busypopFE9F_dmg08_cgb04c_out6576FFFF on DMG in GBHawk", + "oamdma.oamdma_src7F00_busypopFEFF_dmg08_cgb04c_out6576FFEF on CGB_C in GBHawk", + "oamdma.oamdma_src7F00_busypopFEFF_dmg08_cgb04c_out6576FFEF on DMG in GBHawk", + "oamdma.oamdma_src7F00_busypush0001_dmg08_cgb04c_out5576AA34 on CGB_C in GBHawk", + "oamdma.oamdma_src7F00_busypush0001_dmg08_cgb04c_out5576AA34 on DMG in GBHawk", + "oamdma.oamdma_src7F00_busypush8001_dmg08_cgb04c_out65AA1255 on CGB_C in GBHawk", + "oamdma.oamdma_src7F00_busypush8001_dmg08_cgb04c_out65AA1255 on DMG in GBHawk", + "oamdma.oamdma_src7F00_busypushA001_2_dmg08_cgb04c_out5576AAFF on CGB_C in GBHawk", + "oamdma.oamdma_src7F00_busypushA001_2_dmg08_cgb04c_out5576AAFF on DMG in GBHawk", + "oamdma.oamdma_src7F00_busypushA001_dmg08_cgb04c_out5576AA34 on CGB_C in GBHawk", + "oamdma.oamdma_src7F00_busypushA001_dmg08_cgb04c_out5576AA34 on DMG in GBHawk", + "oamdma.oamdma_src7F00_busypushC001_2_dmg08_out55AAFF34_cgb04c_out65AAFF55 on CGB_C in GBHawk", + "oamdma.oamdma_src7F00_busypushC001_2_dmg08_out55AAFF34_cgb04c_out65AAFF55 on DMG in GBHawk", + "oamdma.oamdma_src7F00_busypushC001_dmg08_out55AA1234_cgb04c_out65AA1255 on CGB_C in GBHawk", + "oamdma.oamdma_src7F00_busypushC001_dmg08_out55AA1234_cgb04c_out65AA1255 on DMG in GBHawk", + "oamdma.oamdma_src7F00_busypushE001_dmg08_out55AA1234_cgb04c_out6576AA55 on CGB_C in GBHawk", + "oamdma.oamdma_src7F00_busypushE001_dmg08_out55AA1234_cgb04c_out6576AA55 on DMG in GBHawk", + "oamdma.oamdma_src7F00_busypushF001_dmg08_out55AA1234_cgb04c_out6576AA55 on CGB_C in GBHawk", + "oamdma.oamdma_src7F00_busypushF001_dmg08_out55AA1234_cgb04c_out6576AA55 on DMG in GBHawk", + "oamdma.oamdma_src7F00_busypushFE01_dmg08_out65AA1298_cgb04c_out6576AA98 on DMG in GBHawk", + "oamdma.oamdma_src7F00_busypushFEA1_dmg08_out65768700_cgb04c_out65768734 on CGB_C in GBHawk", + "oamdma.oamdma_src7F00_busypushFF01_dmg08_out657600DF_cgb04c_out657612DF on CGB_C in GBHawk", + "oamdma.oamdma_src8000_busypop7FFF_dmg08_out65765576_cgb04c_out65005576 on CGB_C in GBHawk", + "oamdma.oamdma_src8000_busypop9FFF_2_dmg08_out657665FF_cgb04c_out007665FF on CGB_C in GBHawk", + "oamdma.oamdma_src8000_busypop9FFF_2_dmg08_out657665FF_cgb04c_out007665FF on DMG in GBHawk", + "oamdma.oamdma_src8000_busypop9FFF_dmg08_out657665AA_cgb04c_out007665AA on CGB_C in GBHawk", + "oamdma.oamdma_src8000_busypop9FFF_dmg08_out657665AA_cgb04c_out007665AA on DMG in GBHawk", + "oamdma.oamdma_src8000_busypopBFFF_2_dmg08_cgb04c_out6576FFAA on CGB_C in GBHawk", + "oamdma.oamdma_src8000_busypopBFFF_2_dmg08_cgb04c_out6576FFAA on DMG in GBHawk", + "oamdma.oamdma_src8000_busypopBFFF_dmg08_cgb04c_out657655AA on CGB_C in GBHawk", + "oamdma.oamdma_src8000_busypopBFFF_dmg08_cgb04c_out657655AA on DMG in GBHawk", + "oamdma.oamdma_src8000_busypopDFFF_dmg08_cgb04c_out657655AA on CGB_C in GBHawk", + "oamdma.oamdma_src8000_busypopDFFF_dmg08_cgb04c_out657655AA on DMG in GBHawk", + "oamdma.oamdma_src8000_busypopFE9F_dmg08_cgb04c_out6576FFFF on CGB_C in GBHawk", + "oamdma.oamdma_src8000_busypopFE9F_dmg08_cgb04c_out6576FFFF on DMG in GBHawk", + "oamdma.oamdma_src8000_busypopFEFF_dmg08_cgb04c_out6576FFEF on CGB_C in GBHawk", + "oamdma.oamdma_src8000_busypopFEFF_dmg08_cgb04c_out6576FFEF on DMG in GBHawk", + "oamdma.oamdma_src8000_busypush8001_dmg08_out55761234_cgb04c_out00761234 on CGB_C in GBHawk", + "oamdma.oamdma_src8000_busypush8001_dmg08_out55761234_cgb04c_out00761234 on DMG in GBHawk", + "oamdma.oamdma_src8000_busypushA001_2_dmg08_out65AA12FF_cgb04c_out650012FF on CGB_C in GBHawk", + "oamdma.oamdma_src8000_busypushA001_2_dmg08_out65AA12FF_cgb04c_out650012FF on DMG in GBHawk", + "oamdma.oamdma_src8000_busypushA001_dmg08_out65AA1255_cgb04c_out65001255 on CGB_C in GBHawk", + "oamdma.oamdma_src8000_busypushA001_dmg08_out65AA1255_cgb04c_out65001255 on DMG in GBHawk", + "oamdma.oamdma_src8000_busypushC001_2_dmg08_cgb04c_out6576FF55 on CGB_C in GBHawk", + "oamdma.oamdma_src8000_busypushC001_2_dmg08_cgb04c_out6576FF55 on DMG in GBHawk", + "oamdma.oamdma_src8000_busypushC001_dmg08_cgb04c_out6576AA55 on CGB_C in GBHawk", + "oamdma.oamdma_src8000_busypushC001_dmg08_cgb04c_out6576AA55 on DMG in GBHawk", + "oamdma.oamdma_src8000_busypushE001_dmg08_cgb04c_out6576AA55 on CGB_C in GBHawk", + "oamdma.oamdma_src8000_busypushE001_dmg08_cgb04c_out6576AA55 on DMG in GBHawk", + "oamdma.oamdma_src8000_busypushFEA1_dmg08_out65768700_cgb04c_out65768734 on CGB_C in GBHawk", + "oamdma.oamdma_src8000_busypushFF01_dmg08_out657600DF_cgb04c_out657612DF on CGB_C in GBHawk", + "oamdma.oamdma_src8000_busyreadA000_dmg08_cgb04c_out1 on DMG in GBHawk", + "oamdma.oamdma_src8000_busyreadC000_dmg08_cgb04c_out1 on CGB_C in GBHawk", + "oamdma.oamdma_src8000_busyreadC000_dmg08_cgb04c_out1 on DMG in GBHawk", + "oamdma.oamdma_src8000_busywrite8000_dmg08_cgb04c_out0 on CGB_C in GBHawk", + "oamdma.oamdma_src8000_busywrite8000_dmg08_cgb04c_out0 on DMG in GBHawk", + "oamdma.oamdma_src8000_srcchangeC000_busyread8000_dmg08_cgb04c_out1 on CGB_C in GBHawk", + "oamdma.oamdma_src8000_srcchangeC000_busyread8000_dmg08_cgb04c_out1 on DMG in GBHawk", + "oamdma.oamdma_src9F00_busypop7FFF_dmg08_out65765576_cgb04c_out65005576 on CGB_C in GBHawk", + "oamdma.oamdma_src9F00_busypop9FFF_2_dmg08_out657665FF_cgb04c_out007665FF on CGB_C in GBHawk", + "oamdma.oamdma_src9F00_busypop9FFF_2_dmg08_out657665FF_cgb04c_out007665FF on DMG in GBHawk", + "oamdma.oamdma_src9F00_busypop9FFF_dmg08_out657665AA_cgb04c_out007665AA on CGB_C in GBHawk", + "oamdma.oamdma_src9F00_busypop9FFF_dmg08_out657665AA_cgb04c_out007665AA on DMG in GBHawk", + "oamdma.oamdma_src9F00_busypopBFFF_2_dmg08_cgb04c_out6576FFAA on CGB_C in GBHawk", + "oamdma.oamdma_src9F00_busypopBFFF_2_dmg08_cgb04c_out6576FFAA on DMG in GBHawk", + "oamdma.oamdma_src9F00_busypopBFFF_dmg08_cgb04c_out657655AA on CGB_C in GBHawk", + "oamdma.oamdma_src9F00_busypopBFFF_dmg08_cgb04c_out657655AA on DMG in GBHawk", + "oamdma.oamdma_src9F00_busypopDFFF_dmg08_cgb04c_out657655AA on CGB_C in GBHawk", + "oamdma.oamdma_src9F00_busypopDFFF_dmg08_cgb04c_out657655AA on DMG in GBHawk", + "oamdma.oamdma_src9F00_busypopFE9F_dmg08_cgb04c_out6576FFFF on CGB_C in GBHawk", + "oamdma.oamdma_src9F00_busypopFE9F_dmg08_cgb04c_out6576FFFF on DMG in GBHawk", + "oamdma.oamdma_src9F00_busypopFEFF_dmg08_cgb04c_out6576FFEF on CGB_C in GBHawk", + "oamdma.oamdma_src9F00_busypopFEFF_dmg08_cgb04c_out6576FFEF on DMG in GBHawk", + "oamdma.oamdma_src9F00_busypush8001_dmg08_out55761234_cgb04c_out00761234 on CGB_C in GBHawk", + "oamdma.oamdma_src9F00_busypush8001_dmg08_out55761234_cgb04c_out00761234 on DMG in GBHawk", + "oamdma.oamdma_src9F00_busypushA001_2_dmg08_out65AA12FF_cgb04c_out650012FF on CGB_C in GBHawk", + "oamdma.oamdma_src9F00_busypushA001_2_dmg08_out65AA12FF_cgb04c_out650012FF on DMG in GBHawk", + "oamdma.oamdma_src9F00_busypushA001_dmg08_out65AA1255_cgb04c_out65001255 on CGB_C in GBHawk", + "oamdma.oamdma_src9F00_busypushA001_dmg08_out65AA1255_cgb04c_out65001255 on DMG in GBHawk", + "oamdma.oamdma_src9F00_busypushC001_2_dmg08_cgb04c_out6576FF55 on CGB_C in GBHawk", + "oamdma.oamdma_src9F00_busypushC001_2_dmg08_cgb04c_out6576FF55 on DMG in GBHawk", + "oamdma.oamdma_src9F00_busypushC001_dmg08_cgb04c_out6576AA55 on CGB_C in GBHawk", + "oamdma.oamdma_src9F00_busypushC001_dmg08_cgb04c_out6576AA55 on DMG in GBHawk", + "oamdma.oamdma_src9F00_busypushE001_dmg08_cgb04c_out6576AA55 on CGB_C in GBHawk", + "oamdma.oamdma_src9F00_busypushE001_dmg08_cgb04c_out6576AA55 on DMG in GBHawk", + "oamdma.oamdma_src9F00_busypushFEA1_dmg08_out65768700_cgb04c_out65768734 on CGB_C in GBHawk", + "oamdma.oamdma_src9F00_busypushFF01_dmg08_out657600DF_cgb04c_out657612DF on CGB_C in GBHawk", + "oamdma.oamdma_srcA000_busypop7FFF_dmg08_cgb04c_out657665AA on CGB_C in GBHawk", + "oamdma.oamdma_srcA000_busypop7FFF_dmg08_cgb04c_out657665AA on DMG in GBHawk", + "oamdma.oamdma_srcA000_busypop9FFF_2_dmg08_cgb04c_outFFFF55FF on CGB_C in GBHawk", + "oamdma.oamdma_srcA000_busypop9FFF_2_dmg08_cgb04c_outFFFF55FF on DMG in GBHawk", + "oamdma.oamdma_srcA000_busypop9FFF_dmg08_cgb04c_out65765576 on CGB_C in GBHawk", + "oamdma.oamdma_srcA000_busypop9FFF_dmg08_cgb04c_out65765576 on DMG in GBHawk", + "oamdma.oamdma_srcA000_busypopBFFF_2_dmg08_outFFFFFFFF_cgb04c_outFFFFFFAA on CGB_C in GBHawk", + "oamdma.oamdma_srcA000_busypopBFFF_dmg08_out65766576_cgb04c_out657665AA on CGB_C in GBHawk", + "oamdma.oamdma_srcA000_busypopDFFF_dmg08_out65766576_cgb04c_out657655AA on CGB_C in GBHawk", + "oamdma.oamdma_srcA000_busypopDFFF_dmg08_out65766576_cgb04c_out657655AA on DMG in GBHawk", + "oamdma.oamdma_srcA000_busypopEFFF_dmg08_out65766576_cgb04c_out657655AA on CGB_C in GBHawk", + "oamdma.oamdma_srcA000_busypopEFFF_dmg08_out65766576_cgb04c_out657655AA on DMG in GBHawk", + "oamdma.oamdma_srcA000_busypopFDFF_dmg08_out657665FF_cgb04c_out657655FF on CGB_C in GBHawk", + "oamdma.oamdma_srcA000_busypopFDFF_dmg08_out657665FF_cgb04c_out657655FF on DMG in GBHawk", + "oamdma.oamdma_srcA000_busypopFE9F_dmg08_cgb04c_out6576FFFF on CGB_C in GBHawk", + "oamdma.oamdma_srcA000_busypopFE9F_dmg08_cgb04c_out6576FFFF on DMG in GBHawk", + "oamdma.oamdma_srcA000_busypopFEFF_dmg08_cgb04c_out6576FFEF on CGB_C in GBHawk", + "oamdma.oamdma_srcA000_busypopFEFF_dmg08_cgb04c_out6576FFEF on DMG in GBHawk", + "oamdma.oamdma_srcA000_busypopFFFF_dmg08_cgb04c_out65765576 on CGB_C in GBHawk", + "oamdma.oamdma_srcA000_busypopFFFF_dmg08_cgb04c_out65765576 on DMG in GBHawk", + "oamdma.oamdma_srcA000_busypush0001_dmg08_cgb04c_out5576AA34 on CGB_C in GBHawk", + "oamdma.oamdma_srcA000_busypush0001_dmg08_cgb04c_out5576AA34 on DMG in GBHawk", + "oamdma.oamdma_srcA000_busypush8001_dmg08_cgb04c_out65AA1255 on CGB_C in GBHawk", + "oamdma.oamdma_srcA000_busypush8001_dmg08_cgb04c_out65AA1255 on DMG in GBHawk", + "oamdma.oamdma_srcA000_busypushA001_2_dmg08_cgb04c_out55FFAAFF on CGB_C in GBHawk", + "oamdma.oamdma_srcA000_busypushA001_2_dmg08_cgb04c_out55FFAAFF on DMG in GBHawk", + "oamdma.oamdma_srcA000_busypushA001_dmg08_cgb04c_out5576AA34 on CGB_C in GBHawk", + "oamdma.oamdma_srcA000_busypushA001_dmg08_cgb04c_out5576AA34 on DMG in GBHawk", + "oamdma.oamdma_srcA000_busypushC001_2_dmg08_out55AAFF34_cgb04c_outFFAAFF55 on CGB_C in GBHawk", + "oamdma.oamdma_srcA000_busypushC001_2_dmg08_out55AAFF34_cgb04c_outFFAAFF55 on DMG in GBHawk", + "oamdma.oamdma_srcA000_busypushC001_dmg08_out55AA1234_cgb04c_out65AA1255 on CGB_C in GBHawk", + "oamdma.oamdma_srcA000_busypushC001_dmg08_out55AA1234_cgb04c_out65AA1255 on DMG in GBHawk", + "oamdma.oamdma_srcA000_busypushE001_dmg08_out55AA1234_cgb04c_out6576AA55 on CGB_C in GBHawk", + "oamdma.oamdma_srcA000_busypushE001_dmg08_out55AA1234_cgb04c_out6576AA55 on DMG in GBHawk", + "oamdma.oamdma_srcA000_busypushF001_dmg08_out55AA1234_cgb04c_out6576AA55 on CGB_C in GBHawk", + "oamdma.oamdma_srcA000_busypushF001_dmg08_out55AA1234_cgb04c_out6576AA55 on DMG in GBHawk", + "oamdma.oamdma_srcA000_busypushFE01_dmg08_out65AA1298_cgb04c_out6576AA98 on CGB_C in GBHawk", + "oamdma.oamdma_srcA000_busypushFE01_dmg08_out65AA1298_cgb04c_out6576AA98 on DMG in GBHawk", + "oamdma.oamdma_srcA000_busypushFEA1_dmg08_out65768700_cgb04c_out65768734 on CGB_C in GBHawk", + "oamdma.oamdma_srcA000_busypushFF01_dmg08_out657600DF_cgb04c_out657612DF on CGB_C in GBHawk", + "oamdma.oamdma_srcA000_busyread0000_1_dmg08_cgb04c_out5 on CGB_C in GBHawk", + "oamdma.oamdma_srcA000_busyread0000_1_dmg08_cgb04c_out5 on DMG in GBHawk", + "oamdma.oamdma_srcA000_busyreadC000_1_dmg08_out5_cgb04c_out1 on CGB_C in GBHawk", + "oamdma.oamdma_srcA000_busywrite4000_dmg08_cgb04c_out2 on CGB_C in GBHawk", + "oamdma.oamdma_srcA000_busywrite4000_dmg08_cgb04c_out2 on DMG in GBHawk", + "oamdma.oamdma_srcBF00_busypop7FFF_dmg08_cgb04c_out657665AA on CGB_C in GBHawk", + "oamdma.oamdma_srcBF00_busypop7FFF_dmg08_cgb04c_out657665AA on DMG in GBHawk", + "oamdma.oamdma_srcBF00_busypop9FFF_2_dmg08_cgb04c_outFFFF55FF on CGB_C in GBHawk", + "oamdma.oamdma_srcBF00_busypop9FFF_2_dmg08_cgb04c_outFFFF55FF on DMG in GBHawk", + "oamdma.oamdma_srcBF00_busypop9FFF_dmg08_cgb04c_out65765576 on CGB_C in GBHawk", + "oamdma.oamdma_srcBF00_busypop9FFF_dmg08_cgb04c_out65765576 on DMG in GBHawk", + "oamdma.oamdma_srcBF00_busypopBFFF_2_dmg08_outFFFFFFFF_cgb04c_outFFFFFFAA on CGB_C in GBHawk", + "oamdma.oamdma_srcBF00_busypopBFFF_dmg08_out65766576_cgb04c_out657665AA on CGB_C in GBHawk", + "oamdma.oamdma_srcBF00_busypopDFFF_dmg08_out65766576_cgb04c_out657655AA on CGB_C in GBHawk", + "oamdma.oamdma_srcBF00_busypopDFFF_dmg08_out65766576_cgb04c_out657655AA on DMG in GBHawk", + "oamdma.oamdma_srcBF00_busypopEFFF_dmg08_out65766576_cgb04c_out657655AA on CGB_C in GBHawk", + "oamdma.oamdma_srcBF00_busypopEFFF_dmg08_out65766576_cgb04c_out657655AA on DMG in GBHawk", + "oamdma.oamdma_srcBF00_busypopFDFF_dmg08_out657665FF_cgb04c_out657655FF on DMG in GBHawk", + "oamdma.oamdma_srcBF00_busypopFE9F_dmg08_cgb04c_out6576FFFF on CGB_C in GBHawk", + "oamdma.oamdma_srcBF00_busypopFE9F_dmg08_cgb04c_out6576FFFF on DMG in GBHawk", + "oamdma.oamdma_srcBF00_busypopFEFF_dmg08_cgb04c_out6576FFEF on CGB_C in GBHawk", + "oamdma.oamdma_srcBF00_busypopFEFF_dmg08_cgb04c_out6576FFEF on DMG in GBHawk", + "oamdma.oamdma_srcBF00_busypopFFFF_dmg08_cgb04c_out65765576 on CGB_C in GBHawk", + "oamdma.oamdma_srcBF00_busypopFFFF_dmg08_cgb04c_out65765576 on DMG in GBHawk", + "oamdma.oamdma_srcBF00_busypush0001_dmg08_cgb04c_out5576AA34 on CGB_C in GBHawk", + "oamdma.oamdma_srcBF00_busypush0001_dmg08_cgb04c_out5576AA34 on DMG in GBHawk", + "oamdma.oamdma_srcBF00_busypush8001_dmg08_cgb04c_out65AA1255 on CGB_C in GBHawk", + "oamdma.oamdma_srcBF00_busypush8001_dmg08_cgb04c_out65AA1255 on DMG in GBHawk", + "oamdma.oamdma_srcBF00_busypushA001_2_dmg08_cgb04c_out55FFAAFF on CGB_C in GBHawk", + "oamdma.oamdma_srcBF00_busypushA001_2_dmg08_cgb04c_out55FFAAFF on DMG in GBHawk", + "oamdma.oamdma_srcBF00_busypushA001_dmg08_cgb04c_out5576AA34 on CGB_C in GBHawk", + "oamdma.oamdma_srcBF00_busypushA001_dmg08_cgb04c_out5576AA34 on DMG in GBHawk", + "oamdma.oamdma_srcBF00_busypushC001_2_dmg08_out55AAFF34_cgb04c_outFFAAFF55 on CGB_C in GBHawk", + "oamdma.oamdma_srcBF00_busypushC001_2_dmg08_out55AAFF34_cgb04c_outFFAAFF55 on DMG in GBHawk", + "oamdma.oamdma_srcBF00_busypushC001_dmg08_out55AA1234_cgb04c_out65AA1255 on CGB_C in GBHawk", + "oamdma.oamdma_srcBF00_busypushC001_dmg08_out55AA1234_cgb04c_out65AA1255 on DMG in GBHawk", + "oamdma.oamdma_srcBF00_busypushE001_dmg08_out55AA1234_cgb04c_out6576AA55 on CGB_C in GBHawk", + "oamdma.oamdma_srcBF00_busypushE001_dmg08_out55AA1234_cgb04c_out6576AA55 on DMG in GBHawk", + "oamdma.oamdma_srcBF00_busypushF001_dmg08_out55AA1234_cgb04c_out6576AA55 on CGB_C in GBHawk", + "oamdma.oamdma_srcBF00_busypushF001_dmg08_out55AA1234_cgb04c_out6576AA55 on DMG in GBHawk", + "oamdma.oamdma_srcBF00_busypushFE01_dmg08_out65AA1298_cgb04c_out6576AA98 on DMG in GBHawk", + "oamdma.oamdma_srcBF00_busypushFEA1_dmg08_out65768700_cgb04c_out65768734 on CGB_C in GBHawk", + "oamdma.oamdma_srcBF00_busypushFF01_dmg08_out657600DF_cgb04c_out657612DF on CGB_C in GBHawk", + "oamdma.oamdma_srcC000_busypop7FFF_dmg08_out657665AA_cgb04c_out657655AA on CGB_C in GBHawk", + "oamdma.oamdma_srcC000_busypop7FFF_dmg08_out657665AA_cgb04c_out657655AA on DMG in GBHawk", + "oamdma.oamdma_srcC000_busypop9FFF_2_dmg08_out65765576_cgb04c_out657655FF on CGB_C in GBHawk", + "oamdma.oamdma_srcC000_busypop9FFF_2_dmg08_out65765576_cgb04c_out657655FF on DMG in GBHawk", + "oamdma.oamdma_srcC000_busypop9FFF_dmg08_out65765576_cgb04c_out657655AA on CGB_C in GBHawk", + "oamdma.oamdma_srcC000_busypop9FFF_dmg08_out65765576_cgb04c_out657655AA on DMG in GBHawk", + "oamdma.oamdma_srcC000_busypopDFFF_dmg08_cgb04c_out65766576 on CGB_C in GBHawk", + "oamdma.oamdma_srcC000_busypopDFFF_dmg08_cgb04c_out65766576 on DMG in GBHawk", + "oamdma.oamdma_srcC000_busypopEFFF_dmg08_cgb04c_out65766576 on CGB_C in GBHawk", + "oamdma.oamdma_srcC000_busypopEFFF_dmg08_cgb04c_out65766576 on DMG in GBHawk", + "oamdma.oamdma_srcC000_busypopFDFF_dmg08_cgb04c_out657665FF on CGB_C in GBHawk", + "oamdma.oamdma_srcC000_busypopFDFF_dmg08_cgb04c_out657665FF on DMG in GBHawk", + "oamdma.oamdma_srcC000_busypopFE9F_dmg08_cgb04c_out6576FFFF on CGB_C in GBHawk", + "oamdma.oamdma_srcC000_busypopFE9F_dmg08_cgb04c_out6576FFFF on DMG in GBHawk", + "oamdma.oamdma_srcC000_busypopFEFF_dmg08_cgb04c_out6576FFEF on CGB_C in GBHawk", + "oamdma.oamdma_srcC000_busypopFEFF_dmg08_cgb04c_out6576FFEF on DMG in GBHawk", + "oamdma.oamdma_srcC000_busypopFFFF_dmg08_out65765576_cgb04c_out657655AA on DMG in GBHawk", + "oamdma.oamdma_srcC000_busypush0001_dmg08_out4576AA34_cgb04c_out6576AA34 on DMG in GBHawk", + "oamdma.oamdma_srcC000_busypush8001_dmg08_out65221255_cgb04c_out65761255 on CGB_C in GBHawk", + "oamdma.oamdma_srcC000_busypush8001_dmg08_out65221255_cgb04c_out65761255 on DMG in GBHawk", + "oamdma.oamdma_srcC000_busypushA001_2_dmg08_out4576AAFF_cgb04c_out6576AAFF on CGB_C in GBHawk", + "oamdma.oamdma_srcC000_busypushA001_2_dmg08_out4576AAFF_cgb04c_out6576AAFF on DMG in GBHawk", + "oamdma.oamdma_srcC000_busypushA001_dmg08_out4576AA34_cgb04c_out6576AA55 on CGB_C in GBHawk", + "oamdma.oamdma_srcC000_busypushA001_dmg08_out4576AA34_cgb04c_out6576AA55 on DMG in GBHawk", + "oamdma.oamdma_srcC000_busypushC001_2_dmg08_out4522FF34_cgb04c_out6576FF34 on DMG in GBHawk", + "oamdma.oamdma_srcC000_busypushC001_dmg08_out45221234_cgb04c_out6576AA34 on DMG in GBHawk", + "oamdma.oamdma_srcC000_busypushE001_dmg08_out45221234_cgb04c_out65761234 on CGB_C in GBHawk", + "oamdma.oamdma_srcC000_busypushE001_dmg08_out45221234_cgb04c_out65761234 on DMG in GBHawk", + "oamdma.oamdma_srcC000_busypushF001_dmg08_out45221234_cgb04c_out65761234 on CGB_C in GBHawk", + "oamdma.oamdma_srcC000_busypushF001_dmg08_out45221234_cgb04c_out65761234 on DMG in GBHawk", + "oamdma.oamdma_srcC000_busypushFE01_dmg08_out65221298_cgb04c_out65761298 on CGB_C in GBHawk", + "oamdma.oamdma_srcC000_busypushFE01_dmg08_out65221298_cgb04c_out65761298 on DMG in GBHawk", + "oamdma.oamdma_srcC000_busypushFEA1_dmg08_out65768700_cgb04c_out65768734 on CGB_C in GBHawk", + "oamdma.oamdma_srcC000_busypushFF01_dmg08_out657600DF_cgb04c_out657612DF on CGB_C in GBHawk", + "oamdma.oamdma_srcC000_srambankchange_1_dmg08_out4_cgb04c_out0 on CGB_C in GBHawk", + "oamdma.oamdma_srcDF00_busypop7FFF_dmg08_out657665AA_cgb04c_out657655AA on CGB_C in GBHawk", + "oamdma.oamdma_srcDF00_busypop7FFF_dmg08_out657665AA_cgb04c_out657655AA on DMG in GBHawk", + "oamdma.oamdma_srcDF00_busypop9FFF_2_dmg08_out65765576_cgb04c_out657655FF on CGB_C in GBHawk", + "oamdma.oamdma_srcDF00_busypop9FFF_2_dmg08_out65765576_cgb04c_out657655FF on DMG in GBHawk", + "oamdma.oamdma_srcDF00_busypop9FFF_dmg08_out65765576_cgb04c_out657655AA on CGB_C in GBHawk", + "oamdma.oamdma_srcDF00_busypop9FFF_dmg08_out65765576_cgb04c_out657655AA on DMG in GBHawk", + "oamdma.oamdma_srcDF00_busypopDFFF_dmg08_cgb04c_out65766576 on CGB_C in GBHawk", + "oamdma.oamdma_srcDF00_busypopDFFF_dmg08_cgb04c_out65766576 on DMG in GBHawk", + "oamdma.oamdma_srcDF00_busypopEFFF_dmg08_cgb04c_out65766576 on CGB_C in GBHawk", + "oamdma.oamdma_srcDF00_busypopEFFF_dmg08_cgb04c_out65766576 on DMG in GBHawk", + "oamdma.oamdma_srcDF00_busypopFDFF_dmg08_cgb04c_out657665FF on CGB_C in GBHawk", + "oamdma.oamdma_srcDF00_busypopFDFF_dmg08_cgb04c_out657665FF on DMG in GBHawk", + "oamdma.oamdma_srcDF00_busypopFE9F_dmg08_cgb04c_out6576FFFF on CGB_C in GBHawk", + "oamdma.oamdma_srcDF00_busypopFE9F_dmg08_cgb04c_out6576FFFF on DMG in GBHawk", + "oamdma.oamdma_srcDF00_busypopFEFF_dmg08_cgb04c_out6576FFEF on CGB_C in GBHawk", + "oamdma.oamdma_srcDF00_busypopFEFF_dmg08_cgb04c_out6576FFEF on DMG in GBHawk", + "oamdma.oamdma_srcDF00_busypopFFFF_dmg08_out65765576_cgb04c_out657655AA on DMG in GBHawk", + "oamdma.oamdma_srcDF00_busypush0001_dmg08_out4576AA34_cgb04c_out6576AA34 on DMG in GBHawk", + "oamdma.oamdma_srcDF00_busypush8001_dmg08_out65221255_cgb04c_out65761255 on CGB_C in GBHawk", + "oamdma.oamdma_srcDF00_busypush8001_dmg08_out65221255_cgb04c_out65761255 on DMG in GBHawk", + "oamdma.oamdma_srcDF00_busypushA001_2_dmg08_out4576AAFF_cgb04c_out6576AAFF on CGB_C in GBHawk", + "oamdma.oamdma_srcDF00_busypushA001_2_dmg08_out4576AAFF_cgb04c_out6576AAFF on DMG in GBHawk", + "oamdma.oamdma_srcDF00_busypushA001_dmg08_out4576AA34_cgb04c_out6576AA55 on CGB_C in GBHawk", + "oamdma.oamdma_srcDF00_busypushA001_dmg08_out4576AA34_cgb04c_out6576AA55 on DMG in GBHawk", + "oamdma.oamdma_srcDF00_busypushC001_2_dmg08_out4522FF34_cgb04c_out6576FF34 on DMG in GBHawk", + "oamdma.oamdma_srcDF00_busypushC001_dmg08_out45221234_cgb04c_out6576AA34 on DMG in GBHawk", + "oamdma.oamdma_srcDF00_busypushE001_dmg08_out45221234_cgb04c_out65761234 on CGB_C in GBHawk", + "oamdma.oamdma_srcDF00_busypushE001_dmg08_out45221234_cgb04c_out65761234 on DMG in GBHawk", + "oamdma.oamdma_srcDF00_busypushF001_dmg08_out45221234_cgb04c_out65761234 on CGB_C in GBHawk", + "oamdma.oamdma_srcDF00_busypushF001_dmg08_out45221234_cgb04c_out65761234 on DMG in GBHawk", + "oamdma.oamdma_srcDF00_busypushFE01_dmg08_out65221298_cgb04c_out65761298 on CGB_C in GBHawk", + "oamdma.oamdma_srcDF00_busypushFE01_dmg08_out65221298_cgb04c_out65761298 on DMG in GBHawk", + "oamdma.oamdma_srcDF00_busypushFEA1_dmg08_out65768700_cgb04c_out65768734 on CGB_C in GBHawk", + "oamdma.oamdma_srcDF00_busypushFF01_dmg08_out657600DF_cgb04c_out657612DF on CGB_C in GBHawk", + "oamdma.oamdma_srcE000_busypop7FFF_dmg08_out657665AA_cgb04c_outFFFFFFAA on CGB_C in GBHawk", + "oamdma.oamdma_srcE000_busypop7FFF_dmg08_out657665AA_cgb04c_outFFFFFFAA on DMG in GBHawk", + "oamdma.oamdma_srcE000_busypop9FFF_2_dmg08_out65765576_cgb04c_outFFFF55FF on CGB_C in GBHawk", + "oamdma.oamdma_srcE000_busypop9FFF_2_dmg08_out65765576_cgb04c_outFFFF55FF on DMG in GBHawk", + "oamdma.oamdma_srcE000_busypop9FFF_dmg08_out65765576_cgb04c_outFFFF55FF on CGB_C in GBHawk", + "oamdma.oamdma_srcE000_busypop9FFF_dmg08_out65765576_cgb04c_outFFFF55FF on DMG in GBHawk", + "oamdma.oamdma_srcE000_busypopBFFF_2_dmg08_out65766576_cgb04c_outFFFFFFAA on CGB_C in GBHawk", + "oamdma.oamdma_srcE000_busypopBFFF_dmg08_out65766576_cgb04c_outFFFFFFAA on CGB_C in GBHawk", + "oamdma.oamdma_srcE000_busypopDFFF_dmg08_out65766576_cgb04c_outFFFF55AA on CGB_C in GBHawk", + "oamdma.oamdma_srcE000_busypopDFFF_dmg08_out65766576_cgb04c_outFFFF55AA on DMG in GBHawk", + "oamdma.oamdma_srcE000_busypopEFFF_dmg08_out65766576_cgb04c_outFFFF55AA on CGB_C in GBHawk", + "oamdma.oamdma_srcE000_busypopEFFF_dmg08_out65766576_cgb04c_outFFFF55AA on DMG in GBHawk", + "oamdma.oamdma_srcE000_busypopFDFF_dmg08_out657665FF_cgb04c_outFFFF55FF on CGB_C in GBHawk", + "oamdma.oamdma_srcE000_busypopFDFF_dmg08_out657665FF_cgb04c_outFFFF55FF on DMG in GBHawk", + "oamdma.oamdma_srcE000_busypopFE9F_dmg08_out6576FFFF_cgb04c_outFFFFFFFF on CGB_C in GBHawk", + "oamdma.oamdma_srcE000_busypopFE9F_dmg08_out6576FFFF_cgb04c_outFFFFFFFF on DMG in GBHawk", + "oamdma.oamdma_srcE000_busypopFEFF_dmg08_out6576FFEF_cgb04c_outFFFFFFEF on CGB_C in GBHawk", + "oamdma.oamdma_srcE000_busypopFEFF_dmg08_out6576FFEF_cgb04c_outFFFFFFEF on DMG in GBHawk", + "oamdma.oamdma_srcE000_busypopFF7F_dmg08_out6576FFAA_cgb04c_outFFFFFFAA on CGB_C in GBHawk", + "oamdma.oamdma_srcE000_busypopFFFF_dmg08_out65765576_cgb04c_outFFFF55FF on CGB_C in GBHawk", + "oamdma.oamdma_srcE000_busypopFFFF_dmg08_out65765576_cgb04c_outFFFF55FF on DMG in GBHawk", + "oamdma.oamdma_srcE000_busypush0001_dmg08_out4576AA34_cgb04c_out55FFAA34 on CGB_C in GBHawk", + "oamdma.oamdma_srcE000_busypush0001_dmg08_out4576AA34_cgb04c_out55FFAA34 on DMG in GBHawk", + "oamdma.oamdma_srcE000_busypush8001_dmg08_out65221255_cgb04c_outFFAA1255 on CGB_C in GBHawk", + "oamdma.oamdma_srcE000_busypush8001_dmg08_out65221255_cgb04c_outFFAA1255 on DMG in GBHawk", + "oamdma.oamdma_srcE000_busypushA001_2_dmg08_out4576AAFF_cgb04c_out55FFAAFF on CGB_C in GBHawk", + "oamdma.oamdma_srcE000_busypushA001_2_dmg08_out4576AAFF_cgb04c_out55FFAAFF on DMG in GBHawk", + "oamdma.oamdma_srcE000_busypushA001_dmg08_out4576AA34_cgb04c_out55FFAA34 on CGB_C in GBHawk", + "oamdma.oamdma_srcE000_busypushA001_dmg08_out4576AA34_cgb04c_out55FFAA34 on DMG in GBHawk", + "oamdma.oamdma_srcE000_busypushC001_2_dmg08_out4522FF34_cgb04c_outFFAAFF55 on CGB_C in GBHawk", + "oamdma.oamdma_srcE000_busypushC001_2_dmg08_out4522FF34_cgb04c_outFFAAFF55 on DMG in GBHawk", + "oamdma.oamdma_srcE000_busypushC001_dmg08_out45221234_cgb04c_outFFAA1255 on CGB_C in GBHawk", + "oamdma.oamdma_srcE000_busypushC001_dmg08_out45221234_cgb04c_outFFAA1255 on DMG in GBHawk", + "oamdma.oamdma_srcE000_busypushE001_dmg08_out45221234_cgb04c_outFFFFAA55 on CGB_C in GBHawk", + "oamdma.oamdma_srcE000_busypushE001_dmg08_out45221234_cgb04c_outFFFFAA55 on DMG in GBHawk", + "oamdma.oamdma_srcE000_busypushF001_dmg08_out45221234_cgb04c_outFFFFAA55 on CGB_C in GBHawk", + "oamdma.oamdma_srcE000_busypushF001_dmg08_out45221234_cgb04c_outFFFFAA55 on DMG in GBHawk", + "oamdma.oamdma_srcE000_busypushFE01_dmg08_out65221298_cgb04c_outFFFFAAFF on CGB_C in GBHawk", + "oamdma.oamdma_srcE000_busypushFE01_dmg08_out65221298_cgb04c_outFFFFAAFF on DMG in GBHawk", + "oamdma.oamdma_srcE000_busypushFEA1_dmg08_out65768700_cgb04c_outFFFFFF34 on CGB_C in GBHawk", + "oamdma.oamdma_srcE000_busypushFF01_dmg08_out657600DF_cgb04c_outFFFF12DF on CGB_C in GBHawk", + "oamdma.oamdma_srcE000_busypushFF81_dmg08_out6576FF55_cgb04c_outFFFFFF55 on CGB_C in GBHawk", + "oamdma.oamdma_srcE000_readFE00_dmg08_out2_cgb04c_out0 on CGB_C in GBHawk", + "oamdma.oamdma_srcEF00_busypop7FFF_dmg08_out657665AA_cgb04c_outFFFFFFAA on CGB_C in GBHawk", + "oamdma.oamdma_srcEF00_busypop7FFF_dmg08_out657665AA_cgb04c_outFFFFFFAA on DMG in GBHawk", + "oamdma.oamdma_srcEF00_busypop9FFF_2_dmg08_out65765576_cgb04c_outFFFF55FF on CGB_C in GBHawk", + "oamdma.oamdma_srcEF00_busypop9FFF_2_dmg08_out65765576_cgb04c_outFFFF55FF on DMG in GBHawk", + "oamdma.oamdma_srcEF00_busypop9FFF_dmg08_out65765576_cgb04c_outFFFF55FF on CGB_C in GBHawk", + "oamdma.oamdma_srcEF00_busypop9FFF_dmg08_out65765576_cgb04c_outFFFF55FF on DMG in GBHawk", + "oamdma.oamdma_srcEF00_busypopBFFF_2_dmg08_out65766576_cgb04c_outFFFFFFAA on CGB_C in GBHawk", + "oamdma.oamdma_srcEF00_busypopBFFF_dmg08_out65766576_cgb04c_outFFFFFFAA on CGB_C in GBHawk", + "oamdma.oamdma_srcEF00_busypopDFFF_dmg08_out65766576_cgb04c_outFFFF55AA on CGB_C in GBHawk", + "oamdma.oamdma_srcEF00_busypopDFFF_dmg08_out65766576_cgb04c_outFFFF55AA on DMG in GBHawk", + "oamdma.oamdma_srcEF00_busypopEFFF_dmg08_out65766576_cgb04c_outFFFF55AA on CGB_C in GBHawk", + "oamdma.oamdma_srcEF00_busypopEFFF_dmg08_out65766576_cgb04c_outFFFF55AA on DMG in GBHawk", + "oamdma.oamdma_srcEF00_busypopFDFF_dmg08_out657665FF_cgb04c_outFFFF55FF on CGB_C in GBHawk", + "oamdma.oamdma_srcEF00_busypopFDFF_dmg08_out657665FF_cgb04c_outFFFF55FF on DMG in GBHawk", + "oamdma.oamdma_srcEF00_busypopFE9F_dmg08_out6576FFFF_cgb04c_outFFFFFFFF on CGB_C in GBHawk", + "oamdma.oamdma_srcEF00_busypopFE9F_dmg08_out6576FFFF_cgb04c_outFFFFFFFF on DMG in GBHawk", + "oamdma.oamdma_srcEF00_busypopFEFF_dmg08_out6576FFEF_cgb04c_outFFFFFFEF on CGB_C in GBHawk", + "oamdma.oamdma_srcEF00_busypopFEFF_dmg08_out6576FFEF_cgb04c_outFFFFFFEF on DMG in GBHawk", + "oamdma.oamdma_srcEF00_busypopFF7F_dmg08_out6576FFAA_cgb04c_outFFFFFFAA on CGB_C in GBHawk", + "oamdma.oamdma_srcEF00_busypopFFFF_dmg08_out65765576_cgb04c_outFFFF55FF on CGB_C in GBHawk", + "oamdma.oamdma_srcEF00_busypopFFFF_dmg08_out65765576_cgb04c_outFFFF55FF on DMG in GBHawk", + "oamdma.oamdma_srcEF00_busypush0001_dmg08_out4576AA34_cgb04c_out55FFAA34 on CGB_C in GBHawk", + "oamdma.oamdma_srcEF00_busypush0001_dmg08_out4576AA34_cgb04c_out55FFAA34 on DMG in GBHawk", + "oamdma.oamdma_srcEF00_busypush8001_dmg08_out65221255_cgb04c_outFFAA1255 on CGB_C in GBHawk", + "oamdma.oamdma_srcEF00_busypush8001_dmg08_out65221255_cgb04c_outFFAA1255 on DMG in GBHawk", + "oamdma.oamdma_srcEF00_busypushA001_2_dmg08_out4576AAFF_cgb04c_out55FFAAFF on CGB_C in GBHawk", + "oamdma.oamdma_srcEF00_busypushA001_2_dmg08_out4576AAFF_cgb04c_out55FFAAFF on DMG in GBHawk", + "oamdma.oamdma_srcEF00_busypushA001_dmg08_out4576AA34_cgb04c_out55FFAA34 on CGB_C in GBHawk", + "oamdma.oamdma_srcEF00_busypushA001_dmg08_out4576AA34_cgb04c_out55FFAA34 on DMG in GBHawk", + "oamdma.oamdma_srcEF00_busypushC001_2_dmg08_out4522FF34_cgb04c_outFFAAFF55 on CGB_C in GBHawk", + "oamdma.oamdma_srcEF00_busypushC001_2_dmg08_out4522FF34_cgb04c_outFFAAFF55 on DMG in GBHawk", + "oamdma.oamdma_srcEF00_busypushC001_dmg08_out45221234_cgb04c_outFFAA1255 on CGB_C in GBHawk", + "oamdma.oamdma_srcEF00_busypushC001_dmg08_out45221234_cgb04c_outFFAA1255 on DMG in GBHawk", + "oamdma.oamdma_srcEF00_busypushE001_dmg08_out45221234_cgb04c_outFFFFAA55 on CGB_C in GBHawk", + "oamdma.oamdma_srcEF00_busypushE001_dmg08_out45221234_cgb04c_outFFFFAA55 on DMG in GBHawk", + "oamdma.oamdma_srcEF00_busypushF001_dmg08_out45221234_cgb04c_outFFFFAA55 on CGB_C in GBHawk", + "oamdma.oamdma_srcEF00_busypushF001_dmg08_out45221234_cgb04c_outFFFFAA55 on DMG in GBHawk", + "oamdma.oamdma_srcEF00_busypushFE01_dmg08_out65221298_cgb04c_outFFFFAAFF on CGB_C in GBHawk", + "oamdma.oamdma_srcEF00_busypushFE01_dmg08_out65221298_cgb04c_outFFFFAAFF on DMG in GBHawk", + "oamdma.oamdma_srcEF00_busypushFEA1_dmg08_out65768700_cgb04c_outFFFFFF34 on CGB_C in GBHawk", + "oamdma.oamdma_srcEF00_busypushFF01_dmg08_out657600DF_cgb04c_outFFFF12DF on CGB_C in GBHawk", + "oamdma.oamdma_srcEF00_busypushFF81_dmg08_out6576FF55_cgb04c_outFFFFFF55 on CGB_C in GBHawk", + "oamdma.oamdma_srcF000_busypop7FFF_dmg08_out657665AA_cgb04c_outFFFFFFAA on CGB_C in GBHawk", + "oamdma.oamdma_srcF000_busypop7FFF_dmg08_out657665AA_cgb04c_outFFFFFFAA on DMG in GBHawk", + "oamdma.oamdma_srcF000_busypop9FFF_2_dmg08_out65765576_cgb04c_outFFFF55FF on CGB_C in GBHawk", + "oamdma.oamdma_srcF000_busypop9FFF_2_dmg08_out65765576_cgb04c_outFFFF55FF on DMG in GBHawk", + "oamdma.oamdma_srcF000_busypop9FFF_dmg08_out65765576_cgb04c_outFFFF55FF on CGB_C in GBHawk", + "oamdma.oamdma_srcF000_busypop9FFF_dmg08_out65765576_cgb04c_outFFFF55FF on DMG in GBHawk", + "oamdma.oamdma_srcF000_busypopBFFF_2_dmg08_out65766576_cgb04c_outFFFFFFAA on CGB_C in GBHawk", + "oamdma.oamdma_srcF000_busypopBFFF_dmg08_out65766576_cgb04c_outFFFFFFAA on CGB_C in GBHawk", + "oamdma.oamdma_srcF000_busypopDFFF_dmg08_out65766576_cgb04c_outFFFF55AA on CGB_C in GBHawk", + "oamdma.oamdma_srcF000_busypopDFFF_dmg08_out65766576_cgb04c_outFFFF55AA on DMG in GBHawk", + "oamdma.oamdma_srcF000_busypopEFFF_dmg08_out65766576_cgb04c_outFFFF55AA on CGB_C in GBHawk", + "oamdma.oamdma_srcF000_busypopEFFF_dmg08_out65766576_cgb04c_outFFFF55AA on DMG in GBHawk", + "oamdma.oamdma_srcF000_busypopFDFF_dmg08_out657665FF_cgb04c_outFFFF55FF on CGB_C in GBHawk", + "oamdma.oamdma_srcF000_busypopFDFF_dmg08_out657665FF_cgb04c_outFFFF55FF on DMG in GBHawk", + "oamdma.oamdma_srcF000_busypopFE9F_dmg08_out6576FFFF_cgb04c_outFFFFFFFF on CGB_C in GBHawk", + "oamdma.oamdma_srcF000_busypopFE9F_dmg08_out6576FFFF_cgb04c_outFFFFFFFF on DMG in GBHawk", + "oamdma.oamdma_srcF000_busypopFEFF_dmg08_out6576FFEF_cgb04c_outFFFFFFEF on CGB_C in GBHawk", + "oamdma.oamdma_srcF000_busypopFEFF_dmg08_out6576FFEF_cgb04c_outFFFFFFEF on DMG in GBHawk", + "oamdma.oamdma_srcF000_busypopFF7F_dmg08_out6576FFAA_cgb04c_outFFFFFFAA on CGB_C in GBHawk", + "oamdma.oamdma_srcF000_busypopFFFF_dmg08_out65765576_cgb04c_outFFFF55FF on CGB_C in GBHawk", + "oamdma.oamdma_srcF000_busypopFFFF_dmg08_out65765576_cgb04c_outFFFF55FF on DMG in GBHawk", + "oamdma.oamdma_srcF000_busypush0001_dmg08_out4576AA34_cgb04c_out55FFAA34 on CGB_C in GBHawk", + "oamdma.oamdma_srcF000_busypush0001_dmg08_out4576AA34_cgb04c_out55FFAA34 on DMG in GBHawk", + "oamdma.oamdma_srcF000_busypush8001_dmg08_out65221255_cgb04c_outFFAA1255 on CGB_C in GBHawk", + "oamdma.oamdma_srcF000_busypush8001_dmg08_out65221255_cgb04c_outFFAA1255 on DMG in GBHawk", + "oamdma.oamdma_srcF000_busypushA001_2_dmg08_out4576AAFF_cgb04c_out55FFAAFF on CGB_C in GBHawk", + "oamdma.oamdma_srcF000_busypushA001_2_dmg08_out4576AAFF_cgb04c_out55FFAAFF on DMG in GBHawk", + "oamdma.oamdma_srcF000_busypushA001_dmg08_out4576AA34_cgb04c_out55FFAA34 on CGB_C in GBHawk", + "oamdma.oamdma_srcF000_busypushA001_dmg08_out4576AA34_cgb04c_out55FFAA34 on DMG in GBHawk", + "oamdma.oamdma_srcF000_busypushC001_2_dmg08_out4522FF34_cgb04c_outFFAAFF55 on CGB_C in GBHawk", + "oamdma.oamdma_srcF000_busypushC001_2_dmg08_out4522FF34_cgb04c_outFFAAFF55 on DMG in GBHawk", + "oamdma.oamdma_srcF000_busypushC001_dmg08_out45221234_cgb04c_outFFAA1255 on CGB_C in GBHawk", + "oamdma.oamdma_srcF000_busypushC001_dmg08_out45221234_cgb04c_outFFAA1255 on DMG in GBHawk", + "oamdma.oamdma_srcF000_busypushE001_dmg08_out45221234_cgb04c_outFFFFAA55 on CGB_C in GBHawk", + "oamdma.oamdma_srcF000_busypushE001_dmg08_out45221234_cgb04c_outFFFFAA55 on DMG in GBHawk", + "oamdma.oamdma_srcF000_busypushF001_dmg08_out45221234_cgb04c_outFFFFAA55 on CGB_C in GBHawk", + "oamdma.oamdma_srcF000_busypushF001_dmg08_out45221234_cgb04c_outFFFFAA55 on DMG in GBHawk", + "oamdma.oamdma_srcF000_busypushFE01_dmg08_out65221298_cgb04c_outFFFFAAFF on CGB_C in GBHawk", + "oamdma.oamdma_srcF000_busypushFE01_dmg08_out65221298_cgb04c_outFFFFAAFF on DMG in GBHawk", + "oamdma.oamdma_srcF000_busypushFEA1_dmg08_out65768700_cgb04c_outFFFFFF34 on CGB_C in GBHawk", + "oamdma.oamdma_srcF000_busypushFF01_dmg08_out657600DF_cgb04c_outFFFF12DF on CGB_C in GBHawk", + "oamdma.oamdma_srcF000_busypushFF81_dmg08_out6576FF55_cgb04c_outFFFFFF55 on CGB_C in GBHawk", + "oamdma.oamdma_srcF000_busyread0000_1_dmg08_out9_cgb04c_out0 on CGB_C in GBHawk", + "oamdma.oamdma_srcF000_busyread0000_1_dmg08_out9_cgb04c_out0 on DMG in GBHawk", + "oamdma.oamdma_srcF000_busyread8000_dmg08_cgb04c_out3 on CGB_C in GBHawk", + "oamdma.oamdma_srcF000_busyread8000_dmg08_cgb04c_out3 on DMG in GBHawk", + "oamdma.oamdma_srcF000_busyreadA000_dmg08_out6_cgb04c_out0 on CGB_C in GBHawk", + "oamdma.oamdma_srcFD00_readFE00_dmg08_out2_cgb04c_out0 on CGB_C in GBHawk", + "oamdma.oamdma_srcFE00_busypop7FFF_dmg08_out657665AA_cgb04c_outFFFFFFAA on CGB_C in GBHawk", + "oamdma.oamdma_srcFE00_busypop7FFF_dmg08_out657665AA_cgb04c_outFFFFFFAA on DMG in GBHawk", + "oamdma.oamdma_srcFE00_busypop9FFF_2_dmg08_out65765576_cgb04c_outFFFF55FF on CGB_C in GBHawk", + "oamdma.oamdma_srcFE00_busypop9FFF_2_dmg08_out65765576_cgb04c_outFFFF55FF on DMG in GBHawk", + "oamdma.oamdma_srcFE00_busypop9FFF_dmg08_out65765576_cgb04c_outFFFF55FF on CGB_C in GBHawk", + "oamdma.oamdma_srcFE00_busypop9FFF_dmg08_out65765576_cgb04c_outFFFF55FF on DMG in GBHawk", + "oamdma.oamdma_srcFE00_busypopBFFF_2_dmg08_out65766576_cgb04c_outFFFFFFAA on CGB_C in GBHawk", + "oamdma.oamdma_srcFE00_busypopBFFF_dmg08_out65766576_cgb04c_outFFFFFFAA on CGB_C in GBHawk", + "oamdma.oamdma_srcFE00_busypopDFFF_dmg08_out65766576_cgb04c_outFFFF55AA on CGB_C in GBHawk", + "oamdma.oamdma_srcFE00_busypopDFFF_dmg08_out65766576_cgb04c_outFFFF55AA on DMG in GBHawk", + "oamdma.oamdma_srcFE00_busypopEFFF_dmg08_out65766576_cgb04c_outFFFF55AA on CGB_C in GBHawk", + "oamdma.oamdma_srcFE00_busypopEFFF_dmg08_out65766576_cgb04c_outFFFF55AA on DMG in GBHawk", + "oamdma.oamdma_srcFE00_busypopFDFF_dmg08_out657665FF_cgb04c_outFFFF55FF on CGB_C in GBHawk", + "oamdma.oamdma_srcFE00_busypopFDFF_dmg08_out657665FF_cgb04c_outFFFF55FF on DMG in GBHawk", + "oamdma.oamdma_srcFE00_busypopFE9F_dmg08_out6576FFFF_cgb04c_outFFFFFFFF on CGB_C in GBHawk", + "oamdma.oamdma_srcFE00_busypopFE9F_dmg08_out6576FFFF_cgb04c_outFFFFFFFF on DMG in GBHawk", + "oamdma.oamdma_srcFE00_busypopFEFF_dmg08_out6576FFEF_cgb04c_outFFFFFFEF on CGB_C in GBHawk", + "oamdma.oamdma_srcFE00_busypopFEFF_dmg08_out6576FFEF_cgb04c_outFFFFFFEF on DMG in GBHawk", + "oamdma.oamdma_srcFE00_busypopFF7F_dmg08_out6576FFAA_cgb04c_outFFFFFFAA on CGB_C in GBHawk", + "oamdma.oamdma_srcFE00_busypopFFFF_dmg08_out65765576_cgb04c_outFFFF55FF on CGB_C in GBHawk", + "oamdma.oamdma_srcFE00_busypopFFFF_dmg08_out65765576_cgb04c_outFFFF55FF on DMG in GBHawk", + "oamdma.oamdma_srcFE00_busypush0001_dmg08_out4576AA34_cgb04c_out55FFAA34 on CGB_C in GBHawk", + "oamdma.oamdma_srcFE00_busypush0001_dmg08_out4576AA34_cgb04c_out55FFAA34 on DMG in GBHawk", + "oamdma.oamdma_srcFE00_busypush8001_dmg08_out65221255_cgb04c_outFFAA1255 on CGB_C in GBHawk", + "oamdma.oamdma_srcFE00_busypush8001_dmg08_out65221255_cgb04c_outFFAA1255 on DMG in GBHawk", + "oamdma.oamdma_srcFE00_busypushA001_2_dmg08_out4576AAFF_cgb04c_out55FFAAFF on CGB_C in GBHawk", + "oamdma.oamdma_srcFE00_busypushA001_2_dmg08_out4576AAFF_cgb04c_out55FFAAFF on DMG in GBHawk", + "oamdma.oamdma_srcFE00_busypushA001_dmg08_out4576AA34_cgb04c_out55FFAA34 on CGB_C in GBHawk", + "oamdma.oamdma_srcFE00_busypushA001_dmg08_out4576AA34_cgb04c_out55FFAA34 on DMG in GBHawk", + "oamdma.oamdma_srcFE00_busypushC001_2_dmg08_out4522FF34_cgb04c_outFFAAFF55 on CGB_C in GBHawk", + "oamdma.oamdma_srcFE00_busypushC001_2_dmg08_out4522FF34_cgb04c_outFFAAFF55 on DMG in GBHawk", + "oamdma.oamdma_srcFE00_busypushC001_dmg08_out45221234_cgb04c_outFFAA1255 on CGB_C in GBHawk", + "oamdma.oamdma_srcFE00_busypushC001_dmg08_out45221234_cgb04c_outFFAA1255 on DMG in GBHawk", + "oamdma.oamdma_srcFE00_busypushE001_dmg08_out45221234_cgb04c_outFFFFAA55 on CGB_C in GBHawk", + "oamdma.oamdma_srcFE00_busypushE001_dmg08_out45221234_cgb04c_outFFFFAA55 on DMG in GBHawk", + "oamdma.oamdma_srcFE00_busypushF001_dmg08_out45221234_cgb04c_outFFFFAA55 on CGB_C in GBHawk", + "oamdma.oamdma_srcFE00_busypushF001_dmg08_out45221234_cgb04c_outFFFFAA55 on DMG in GBHawk", + "oamdma.oamdma_srcFE00_busypushFE01_dmg08_out65221298_cgb04c_outFFFFAAFF on CGB_C in GBHawk", + "oamdma.oamdma_srcFE00_busypushFE01_dmg08_out65221298_cgb04c_outFFFFAAFF on DMG in GBHawk", + "oamdma.oamdma_srcFE00_busypushFEA1_dmg08_out65768700_cgb04c_outFFFFFF34 on CGB_C in GBHawk", + "oamdma.oamdma_srcFE00_busypushFF01_dmg08_out657600DF_cgb04c_outFFFF12DF on CGB_C in GBHawk", + "oamdma.oamdma_srcFE00_busypushFF81_dmg08_out6576FF55_cgb04c_outFFFFFF55 on CGB_C in GBHawk", + "oamdma.oamdma_srcFE00_busyread0000_dmg08_cgb04c_out0 on CGB_C in GBHawk", + "oamdma.oamdma_srcFE00_busyread0000_dmg08_cgb04c_out0 on DMG in GBHawk", + "oamdma.oamdma_srcFE00_busyread8000_dmg08_cgb04c_out3 on CGB_C in GBHawk", + "oamdma.oamdma_srcFE00_busyread8000_dmg08_cgb04c_out3 on DMG in GBHawk", + "oamdma.oamdma_srcFE00_busyreadA000_dmg08_cgb04c_out0 on CGB_C in GBHawk", + "oamdma.oamdma_srcFE00_busyreadA000_dmg08_cgb04c_out0 on DMG in GBHawk", + "oamdma.oamdma_srcFE00_readFE00_dmg08_cgb04c_out0 on CGB_C in GBHawk", + "oamdma.oamdma_srcFE00_readFE00_dmg08_cgb04c_out0 on DMG in GBHawk", + "oamdma.oamdma_srcFF00_busypop7FFF_dmg08_out657665AA_cgb04c_outFFFFFFAA on CGB_C in GBHawk", // completely blank + "oamdma.oamdma_srcFF00_busypop7FFF_dmg08_out657665AA_cgb04c_outFFFFFFAA on DMG in GBHawk", // Nintendo logo with dots + "oamdma.oamdma_srcFF00_busypop9FFF_2_dmg08_out65765576_cgb04c_outFFFF55FF on CGB_C in GBHawk", // completely blank + "oamdma.oamdma_srcFF00_busypop9FFF_2_dmg08_out65765576_cgb04c_outFFFF55FF on DMG in GBHawk", // stuck on Nintendo logo + "oamdma.oamdma_srcFF00_busypop9FFF_dmg08_out65765576_cgb04c_outFFFF55FF on CGB_C in GBHawk", // completely blank + "oamdma.oamdma_srcFF00_busypop9FFF_dmg08_out65765576_cgb04c_outFFFF55FF on DMG in GBHawk", // stuck on Nintendo logo + "oamdma.oamdma_srcFF00_busypopBFFF_2_dmg08_out65766576_cgb04c_outFFFFFFAA on CGB_C in GBHawk", // completely blank + "oamdma.oamdma_srcFF00_busypopBFFF_2_dmg08_out65766576_cgb04c_outFFFFFFAA on DMG in GBHawk", // stuck on Nintendo logo + "oamdma.oamdma_srcFF00_busypopBFFF_dmg08_out65766576_cgb04c_outFFFFFFAA on CGB_C in GBHawk", // completely blank + "oamdma.oamdma_srcFF00_busypopBFFF_dmg08_out65766576_cgb04c_outFFFFFFAA on DMG in GBHawk", // stuck on Nintendo logo + "oamdma.oamdma_srcFF00_busypopDFFF_dmg08_out65766576_cgb04c_outFFFF55AA on CGB_C in GBHawk", // completely blank + "oamdma.oamdma_srcFF00_busypopDFFF_dmg08_out65766576_cgb04c_outFFFF55AA on DMG in GBHawk", // stuck on Nintendo logo + "oamdma.oamdma_srcFF00_busypopEFFF_dmg08_out65766576_cgb04c_outFFFF55AA on CGB_C in GBHawk", // completely blank + "oamdma.oamdma_srcFF00_busypopEFFF_dmg08_out65766576_cgb04c_outFFFF55AA on DMG in GBHawk", // stuck on Nintendo logo + "oamdma.oamdma_srcFF00_busypopFDFF_dmg08_out657665FF_cgb04c_outFFFF55FF on CGB_C in GBHawk", // completely blank + "oamdma.oamdma_srcFF00_busypopFDFF_dmg08_out657665FF_cgb04c_outFFFF55FF on DMG in GBHawk", // stuck on Nintendo logo + "oamdma.oamdma_srcFF00_busypopFE9F_dmg08_out6576FFFF_cgb04c_outFFFFFFFF on CGB_C in GBHawk", // completely blank + "oamdma.oamdma_srcFF00_busypopFE9F_dmg08_out6576FFFF_cgb04c_outFFFFFFFF on DMG in GBHawk", // stuck on Nintendo logo + "oamdma.oamdma_srcFF00_busypopFEFF_dmg08_out6576FFEF_cgb04c_outFFFFFFEF on CGB_C in GBHawk", // completely blank + "oamdma.oamdma_srcFF00_busypopFEFF_dmg08_out6576FFEF_cgb04c_outFFFFFFEF on DMG in GBHawk", // stuck on Nintendo logo + "oamdma.oamdma_srcFF00_busypopFF7F_dmg08_out6576FFAA_cgb04c_outFFFFFFAA on CGB_C in GBHawk", // completely blank + "oamdma.oamdma_srcFF00_busypopFF7F_dmg08_out6576FFAA_cgb04c_outFFFFFFAA on DMG in GBHawk", // stuck on Nintendo logo + "oamdma.oamdma_srcFF00_busypopFFFF_dmg08_out65765576_cgb04c_outFFFF55FF on CGB_C in GBHawk", // completely blank + "oamdma.oamdma_srcFF00_busypopFFFF_dmg08_out65765576_cgb04c_outFFFF55FF on DMG in GBHawk", // stuck on Nintendo logo + "oamdma.oamdma_srcFF00_busypush0001_dmg08_out4576AA34_cgb04c_out55FFAA34 on CGB_C in GBHawk", // completely blank + "oamdma.oamdma_srcFF00_busypush0001_dmg08_out4576AA34_cgb04c_out55FFAA34 on DMG in GBHawk", // stuck on Nintendo logo + "oamdma.oamdma_srcFF00_busypush8001_dmg08_out65221255_cgb04c_outFFAA1255 on CGB_C in GBHawk", // completely blank + "oamdma.oamdma_srcFF00_busypush8001_dmg08_out65221255_cgb04c_outFFAA1255 on DMG in GBHawk", // Nintendo logo with dots + "oamdma.oamdma_srcFF00_busypushA001_2_dmg08_out4576AAFF_cgb04c_out55FFAAFF on CGB_C in GBHawk", // completely blank + "oamdma.oamdma_srcFF00_busypushA001_2_dmg08_out4576AAFF_cgb04c_out55FFAAFF on DMG in GBHawk", // stuck on Nintendo logo + "oamdma.oamdma_srcFF00_busypushA001_dmg08_out4576AA34_cgb04c_out55FFAA34 on CGB_C in GBHawk", // completely blank + "oamdma.oamdma_srcFF00_busypushA001_dmg08_out4576AA34_cgb04c_out55FFAA34 on DMG in GBHawk", // stuck on Nintendo logo + "oamdma.oamdma_srcFF00_busypushC001_2_dmg08_out4522FF34_cgb04c_outFFAAFF55 on CGB_C in GBHawk", // completely blank + "oamdma.oamdma_srcFF00_busypushC001_2_dmg08_out4522FF34_cgb04c_outFFAAFF55 on DMG in GBHawk", // stuck on Nintendo logo + "oamdma.oamdma_srcFF00_busypushC001_dmg08_out45221234_cgb04c_outFFAA1255 on CGB_C in GBHawk", // completely blank + "oamdma.oamdma_srcFF00_busypushC001_dmg08_out45221234_cgb04c_outFFAA1255 on DMG in GBHawk", // stuck on Nintendo logo + "oamdma.oamdma_srcFF00_busypushE001_dmg08_out45221234_cgb04c_outFFFFAA55 on CGB_C in GBHawk", // completely blank + "oamdma.oamdma_srcFF00_busypushE001_dmg08_out45221234_cgb04c_outFFFFAA55 on DMG in GBHawk", // stuck on Nintendo logo + "oamdma.oamdma_srcFF00_busypushF001_dmg08_out45221234_cgb04c_outFFFFAA55 on CGB_C in GBHawk", // completely blank + "oamdma.oamdma_srcFF00_busypushF001_dmg08_out45221234_cgb04c_outFFFFAA55 on DMG in GBHawk", // stuck on Nintendo logo + "oamdma.oamdma_srcFF00_busypushFE01_dmg08_out65221298_cgb04c_outFFFFAAFF on CGB_C in GBHawk", // completely blank + "oamdma.oamdma_srcFF00_busypushFE01_dmg08_out65221298_cgb04c_outFFFFAAFF on DMG in GBHawk", // stuck on Nintendo logo + "oamdma.oamdma_srcFF00_busypushFEA1_dmg08_out65768700_cgb04c_outFFFFFF34 on CGB_C in GBHawk", // completely blank + "oamdma.oamdma_srcFF00_busypushFEA1_dmg08_out65768700_cgb04c_outFFFFFF34 on DMG in GBHawk", // stuck on Nintendo logo + "oamdma.oamdma_srcFF00_busypushFF01_dmg08_out657600DF_cgb04c_outFFFF12DF on CGB_C in GBHawk", // completely blank + "oamdma.oamdma_srcFF00_busypushFF01_dmg08_out657600DF_cgb04c_outFFFF12DF on DMG in GBHawk", // stuck on Nintendo logo + "oamdma.oamdma_srcFF00_busypushFF81_dmg08_out6576FF55_cgb04c_outFFFFFF55 on CGB_C in GBHawk", // completely blank + "oamdma.oamdma_srcFF00_busypushFF81_dmg08_out6576FF55_cgb04c_outFFFFFF55 on DMG in GBHawk", // stuck on Nintendo logo + "oamdma.oamdma_srcFF00_busyread0000_dmg08_out1_cgb04c_out0 on CGB_C in GBHawk", + "oamdma.oamdma_srcFF00_busyread0000_dmg08_out1_cgb04c_out0 on DMG in GBHawk", + "oamdma.oamdma_srcFF00_busyread8000_dmg08_cgb04c_out3 on CGB_C in GBHawk", + "oamdma.oamdma_srcFF00_busyread8000_dmg08_cgb04c_out3 on DMG in GBHawk", + "oamdma.oamdma_srcFF00_busyreadA000_dmg08_out1_cgb04c_out0 on CGB_C in GBHawk", + "oamdma.oamdma_srcFF00_busyreadA000_dmg08_out1_cgb04c_out0 on DMG in GBHawk", + "oamdma.oamdma_srcFF00_readFE00_dmg08_out1_cgb04c_out0 on CGB_C in GBHawk", + "oamdma.oamdma_srcFF00_readFE00_dmg08_out1_cgb04c_out0 on DMG in GBHawk", + "oamdma.oamdma_srcFF00_readFE45_dmg08_out1_cgb04c_out0 on CGB_C in GBHawk", + "oamdma.oamdma_srcFF00_readFE45_dmg08_out1_cgb04c_out0 on DMG in GBHawk", + "oamdma.oamdmasrc80_halt_lycirq_read8000_dmg08_cgb04c_out81 on DMG in GBHawk", + "oamdma.oamdmasrc80_halt_m2irq_read8000_dmg08_cgb04c_out81 on DMG in GBHawk", + "oamdma.oamdmasrcC000_hdmasrc0000_cgb04c_out0A940C0D on CGB_C in GBHawk", + "scx_during_m3.scx_m3_extend_1_dmg08_cgb04c_out3 on CGB_C in GBHawk", + "scx_during_m3.scx_m3_extend_1_dmg08_cgb04c_out3 on DMG in GBHawk", + "scx_during_m3.scx_m3_extend_ds_1_cgb04c_out3 on CGB_C in GBHawk", + "serial.start_late_div_write_wait_read_if_1a_dmg08_cgb04c_outE0 on CGB_C in GBHawk", + "serial.start_late_div_write_wait_read_if_1a_dmg08_cgb04c_outE0 on DMG in GBHawk", + "serial.start_late_div_write_wait_read_if_2b_dmg08_cgb04c_outE8 on CGB_C in GBHawk", + "serial.start_late_div_write_wait_read_if_2b_dmg08_cgb04c_outE8 on DMG in GBHawk", + "serial.start_late_div_write_wait_read_if_3a_dmg08_cgb04c_outE0 on CGB_C in GBHawk", + "serial.start_late_div_write_wait_read_if_3a_dmg08_cgb04c_outE0 on DMG in GBHawk", + "serial.start_late_div_write_wait_read_if_4_dmg08_cgb04c_outE8 on CGB_C in GBHawk", + "serial.start_late_div_write_wait_read_if_4_dmg08_cgb04c_outE8 on DMG in GBHawk", + "serial.start_wait_restart_read_if_2_dmg08_cgb04c_outE8 on CGB_C in GBHawk", + "serial.start_wait_restart_read_if_2_dmg08_cgb04c_outE8 on DMG in GBHawk", + "serial.start_wait_trigger_int8_read_if_2_dmg08_outE8_cgb04c_outE0 on CGB_C in GBHawk", + "serial.start83_late_div_write_wait_read_if_2b_cgb04c_outE8 on CGB_C in GBHawk", + "sound.ch1_init_reset_sweep_counter_timing_nr52_1_dmg08_cgb04c_out1 on DMG in GBHawk", + "sound.ch1_init_reset_sweep_counter_timing_nr52_3_dmg08_out0_cgb04c_out1 on CGB_C in GBHawk", + "sound.ch2_late_div_write_nr52_ds_1b_cgb04c_outF0 on CGB_C in GBHawk", + "sound.ch2_late_div_write_nr52_ds_2b_cgb04c_outF0 on CGB_C in GBHawk", + "speedchange.speedchange_ch2_nr52_1a_cgb04c_outF2 on CGB_C in GBHawk", + "speedchange.speedchange_ch2_nr52_2a_cgb04c_outF2 on CGB_C in GBHawk", + "speedchange.speedchange_tima02_2a_cgb04c_out03 on CGB_C in GBHawk", + "speedchange.speedchange_tima02_2b_cgb04c_out04 on CGB_C in GBHawk", + "speedchange.speedchange_tima03_2a_cgb04c_out01 on CGB_C in GBHawk", + "speedchange.speedchange_tima03_2b_cgb04c_out02 on CGB_C in GBHawk", + "speedchange.speedchange2_ch2_nr52_ds_1a_cgb04c_outF2 on CGB_C in GBHawk", + "speedchange.speedchange2_ch2_nr52_ds_2a_cgb04c_outF2 on CGB_C in GBHawk", + "speedchange.speedchange2_frame1_m2int_m3stat_scx2_2_cgb04c_out0 on CGB_C in GBHawk", + "speedchange.speedchange2_lcdoff_m2int_m3stat_scx2_2_cgb04c_out0 on CGB_C in GBHawk", + "speedchange.speedchange2_lcdoff_nop_m2int_m3stat_scx1_1_cgb04c_out3 on CGB_C in GBHawk", + "speedchange.speedchange2_lcdoff_nopx2_m2int_m3stat_scx2_2_cgb04c_out0 on CGB_C in GBHawk", + "speedchange.speedchange2_ly44_m3_m3stat_scx3_1_cgb04c_outC3 on CGB_C in GBHawk", + "speedchange.speedchange2_ly44_m3_nop_m3stat_scx1_1_cgb04c_outC3 on CGB_C in GBHawk", + "speedchange.speedchange2_ly44_m3_nopx2_m3stat_scx3_1_cgb04c_outC3 on CGB_C in GBHawk", + "speedchange.speedchange2_m2int_m3stat_scx2_2_cgb04c_out0 on CGB_C in GBHawk", + "speedchange.speedchange2_nop_lcdoff_m2int_m3stat_scx2_2_cgb04c_out0 on CGB_C in GBHawk", + "speedchange.speedchange2_nop_lcdoff_nop_m2int_m3stat_scx1_1_cgb04c_out3 on CGB_C in GBHawk", + "speedchange.speedchange2_nop_lcdoff_nopx2_m2int_m3stat_scx2_2_cgb04c_out0 on CGB_C in GBHawk", + "speedchange.speedchange2_nop_ly44_m3_m3stat_scx3_1_cgb04c_outC3 on CGB_C in GBHawk", + "speedchange.speedchange2_nop_ly44_m3_nop_m3stat_scx1_1_cgb04c_outC3 on CGB_C in GBHawk", + "speedchange.speedchange2_nop_m2int_m3stat_scx1_1_cgb04c_out3 on CGB_C in GBHawk", + "speedchange.speedchange2_tima02_2a_cgb04c_out03 on CGB_C in GBHawk", + "speedchange.speedchange2_tima02_2b_cgb04c_out04 on CGB_C in GBHawk", + "speedchange.speedchange2_tima03_2a_cgb04c_out01 on CGB_C in GBHawk", + "speedchange.speedchange2_tima03_2b_cgb04c_out02 on CGB_C in GBHawk", + "speedchange.speedchange3_ly44_m3_m3stat_scx2_1_cgb04c_outC3 on CGB_C in GBHawk", + "speedchange.speedchange3_ly44_m3_nop_m3stat_scx2_1_cgb04c_outC3 on CGB_C in GBHawk", + "speedchange.speedchange3_nop_ly44_m3_m3stat_scx2_1_cgb04c_outC3 on CGB_C in GBHawk", + "speedchange.speedchange4_ly44_m3_m3stat_scx2_1_cgb04c_outC3 on CGB_C in GBHawk", + "speedchange.speedchange4_ly44_m3_nop_m3stat_scx4_1_cgb04c_outC3 on CGB_C in GBHawk", + "speedchange.speedchange4_nop_ly44_m3_m3stat_scx2_1_cgb04c_outC3 on CGB_C in GBHawk", + "speedchange.speedchange5_ch2_nr52_1a_cgb04c_outF2 on CGB_C in GBHawk", + "speedchange.speedchange5_ch2_nr52_2a_cgb04c_outF2 on CGB_C in GBHawk", + "speedchange.speedchange5_ly44_m3_m3stat_scx1_1_cgb04c_outC3 on CGB_C in GBHawk", + "speedchange.speedchange5_ly44_m3_m3stat_scx2_1_cgb04c_outC3 on CGB_C in GBHawk", + "speedchange.speedchange5_ly44_m3_nop_m3stat_scx1_1_cgb04c_outC3 on CGB_C in GBHawk", + "speedchange.speedchange5_ly44_m3_nop_m3stat_scx2_1_cgb04c_outC3 on CGB_C in GBHawk", + "speedchange.speedchange5_nop_ly44_m3_m3stat_scx1_1_cgb04c_outC3 on CGB_C in GBHawk", + "speedchange.speedchange5_nop_ly44_m3_m3stat_scx2_1_cgb04c_outC3 on CGB_C in GBHawk", + "sprites.10spritesPrLine_10xposA7_m0irq_2_dmg08_cgb04c_out2 on CGB_C in GBHawk", + "sprites.10spritesPrLine_10xposA7_m0irq_2_dmg08_cgb04c_out2 on DMG in GBHawk", + "sprites.late_disable_1_dmg08_out0 on DMG in GBHawk", + "sprites.late_sizechange_2_dmg08_out0_cgb04c_out3 on CGB_C in GBHawk", + "sprites.late_sizechange_3_dmg08_cgb04c_out3 on CGB_C in GBHawk", + "sprites.late_sizechange_3_dmg08_cgb04c_out3 on DMG in GBHawk", + "sprites.late_sizechange_ds_2_cgb04c_out3 on CGB_C in GBHawk", + "sprites.late_sizechange_sp00_2_dmg08_cgb04c_out3 on CGB_C in GBHawk", + "sprites.late_sizechange_sp00_2_dmg08_cgb04c_out3 on DMG in GBHawk", + "sprites.late_sizechange_sp00_4_dmg08_cgb04c_out0 on CGB_C in GBHawk", + "sprites.late_sizechange_sp00_4_dmg08_cgb04c_out0 on DMG in GBHawk", + "sprites.late_sizechange_sp00_ds_2_cgb04c_out3 on CGB_C in GBHawk", + "sprites.late_sizechange_sp01_2_dmg08_out0_cgb04c_out3 on CGB_C in GBHawk", + "sprites.late_sizechange_sp01_ds_2_cgb04c_out3 on CGB_C in GBHawk", + "sprites.late_sizechange_sp02_2_dmg08_cgb04c_out3 on CGB_C in GBHawk", + "sprites.late_sizechange_sp02_2_dmg08_cgb04c_out3 on DMG in GBHawk", + "sprites.late_sizechange_sp39_1_dmg08_cgb04c_out0 on CGB_C in GBHawk", + "sprites.late_sizechange_sp39_1_dmg08_cgb04c_out0 on DMG in GBHawk", + "sprites.late_sizechange_sp39_2_dmg08_out0_cgb04c_out3 on DMG in GBHawk", + "sprites.late_sizechange_sp39_ds_1_cgb04c_out0 on CGB_C in GBHawk", + "sprites.late_sizechange2_sp00_2_dmg08_cgb04c_out0 on CGB_C in GBHawk", + "sprites.late_sizechange2_sp00_2_dmg08_cgb04c_out0 on DMG in GBHawk", + "sprites.late_sizechange2_sp00_ds_2_cgb04c_out0 on CGB_C in GBHawk", + "sprites.late_sizechange2_sp01_ds_2_cgb04c_out0 on CGB_C in GBHawk", + "sprites.late_sizechange2_sp02_2_dmg08_cgb04c_out0 on CGB_C in GBHawk", + "sprites.late_sizechange2_sp02_2_dmg08_cgb04c_out0 on DMG in GBHawk", + "sprites.late_sizechange2_sp39_1_dmg08_cgb04c_out3 on CGB_C in GBHawk", + "sprites.late_sizechange2_sp39_1_dmg08_cgb04c_out3 on DMG in GBHawk", + "sprites.late_sizechange2_sp39_ds_1_cgb04c_out3 on CGB_C in GBHawk", + "sprites.space.10spritesPrLine_late_scx4_ds_1_cgb04c_out0 on CGB_C in GBHawk", + "sprites.sprite_late_disable_spx18_1_dmg08_out0 on DMG in GBHawk", + "sprites.sprite_late_disable_spx19_1_dmg08_out0 on DMG in GBHawk", + "sprites.sprite_late_disable_spx1A_1_dmg08_out0 on DMG in GBHawk", + "sprites.sprite_late_disable_spx1B_1_dmg08_out0 on DMG in GBHawk", + "sprites.sprite_late_enable_spx18_2_dmg08_out0 on DMG in GBHawk", + "sprites.sprite_late_enable_spx1A_2_dmg08_out0 on DMG in GBHawk", + "sprites.sprite_late_enable_spx1B_2_dmg08_out0 on DMG in GBHawk", + "sprites.sprite_late_late_disable_spx18_1_dmg08_out0 on DMG in GBHawk", + "sprites.sprite_late_late_disable_spx19_1_dmg08_out0 on DMG in GBHawk", + "sprites.sprite_late_late_disable_spx1A_1_dmg08_out0 on DMG in GBHawk", + "sprites.sprite_late_late_disable_spx1B_1_dmg08_out0 on DMG in GBHawk", + "tima.tc00_irq_late_retrigger_2_dmg08_outE4_cgb04c_outE0 on CGB_C in GBHawk", + "vram_m3.preread_lcdoffset2_1_cgb04c_out0 on CGB_C in GBHawk", + "vram_m3.prewrite_ds_1_cgb04c_out1 on CGB_C in GBHawk", + "vram_m3.prewrite_lcdoffset2_1_cgb04c_out1 on CGB_C in GBHawk", + "vramw_m3end.vramw_m3end_ds_5_cgb04c_out1 on CGB_C in GBHawk", + "vramw_m3end.vramw_m3end_scx3_5_dmg08_cgb04c_out3 on CGB_C in GBHawk", + "vramw_m3end.vramw_m3end_scx3_5_dmg08_cgb04c_out3 on DMG in GBHawk", + "window.arg.late_enable_afterVblank_4_dmg08_out3_cgb04c_out0 on CGB_C in GBHawk", + "window.arg.late_enable_afterVblank_5_dmg08_cgb04c_out0 on CGB_C in GBHawk", + "window.arg.late_enable_afterVblank_5_dmg08_cgb04c_out0 on DMG in GBHawk", + "window.arg.late_scx_late_wy_FFto4_ly4_wx00_1_dmg08_cgb04c_out3 on CGB_C in GBHawk", + "window.arg.late_scx_late_wy_FFto4_ly4_wx00_1_dmg08_cgb04c_out3 on DMG in GBHawk", + "window.arg.late_scx_late_wy_FFto4_ly4_wx00_2_dmg08_out3_cgb04c_out0 on DMG in GBHawk", + "window.arg.late_scx_late_wy_FFto4_ly4_wx20_1_dmg08_cgb04c_out3 on CGB_C in GBHawk", + "window.arg.late_scx_late_wy_FFto4_ly4_wx20_1_dmg08_cgb04c_out3 on DMG in GBHawk", + "window.arg.late_scx_late_wy_FFto4_ly4_wx20_2_dmg08_out3_cgb04c_out0 on DMG in GBHawk", + "window.arg.late_wx_late_wy_FFto2_ly2_2_dmg08_cgb04c_out3 on CGB_C in GBHawk", + "window.arg.late_wx_late_wy_FFto2_ly2_2_dmg08_cgb04c_out3 on DMG in GBHawk", + "window.arg.late_wy_10to0_ly1_1_dmg08_cgb04c_out3 on CGB_C in GBHawk", + "window.arg.late_wy_10to0_ly1_1_dmg08_cgb04c_out3 on DMG in GBHawk", + "window.arg.late_wy_10to0_ly1_2_dmg08_out3_cgb04c_out0 on DMG in GBHawk", + "window.arg.late_wy_10to1_ly1_1_dmg08_cgb04c_out3 on CGB_C in GBHawk", + "window.arg.late_wy_10to1_ly1_1_dmg08_cgb04c_out3 on DMG in GBHawk", + "window.arg.late_wy_10to1_ly1_2_dmg08_out3_cgb04c_out0 on DMG in GBHawk", + "window.arg.late_wy_1toFF_2_dmg08_out0_cgb04c_out3 on CGB_C in GBHawk", + "window.arg.late_wy_1toFF_3_dmg08_cgb04c_out3 on CGB_C in GBHawk", + "window.arg.late_wy_1toFF_3_dmg08_cgb04c_out3 on DMG in GBHawk", + "window.arg.late_wy_1toFF_ds_2_cgb04c_out3 on CGB_C in GBHawk", + "window.arg.late_wy_1toFF_ds_lcdoffset1_2_cgb04c_out3 on CGB_C in GBHawk", + "window.arg.late_wy_1toFF_lcdoffset1_2_cgb04c_out3 on CGB_C in GBHawk", + "window.arg.late_wy_2_dmg08_cgb04c_out3 on CGB_C in GBHawk", + "window.arg.late_wy_2_dmg08_cgb04c_out3 on DMG in GBHawk", + "window.arg.late_wy_2toFF_2_dmg08_out0_cgb04c_out3 on CGB_C in GBHawk", + "window.arg.late_wy_2toFF_3_dmg08_cgb04c_out3 on CGB_C in GBHawk", + "window.arg.late_wy_2toFF_3_dmg08_cgb04c_out3 on DMG in GBHawk", + "window.arg.late_wy_FFto0_ly0_1_dmg08_cgb04c_out3 on CGB_C in GBHawk", + "window.arg.late_wy_FFto0_ly0_1_dmg08_cgb04c_out3 on DMG in GBHawk", + "window.arg.late_wy_FFto0_ly0_2_dmg08_out3_cgb04c_out0 on DMG in GBHawk", + "window.arg.late_wy_FFto0_ly2_1_dmg08_cgb04c_out3 on CGB_C in GBHawk", + "window.arg.late_wy_FFto0_ly2_1_dmg08_cgb04c_out3 on DMG in GBHawk", + "window.arg.late_wy_FFto0_ly2_2_dmg08_out3_cgb04c_out0 on DMG in GBHawk", + "window.arg.late_wy_FFto0_ly2_ds_1_cgb04c_out3 on CGB_C in GBHawk", + "window.arg.late_wy_FFto1_ly2_1_dmg08_cgb04c_out3 on CGB_C in GBHawk", + "window.arg.late_wy_FFto1_ly2_1_dmg08_cgb04c_out3 on DMG in GBHawk", + "window.arg.late_wy_FFto1_ly2_2_dmg08_out3_cgb04c_out0 on DMG in GBHawk", + "window.arg.late_wy_FFto2_ly2_1_dmg08_cgb04c_out3 on CGB_C in GBHawk", + "window.arg.late_wy_FFto2_ly2_1_dmg08_cgb04c_out3 on DMG in GBHawk", + "window.arg.late_wy_FFto2_ly2_2_dmg08_out3_cgb04c_out0 on DMG in GBHawk", + "window.arg.late_wy_FFto2_ly2_ds_1_cgb04c_out3 on CGB_C in GBHawk", + "window.arg.late_wy_FFto2_ly2_scx2_1_dmg08_cgb04c_out3 on CGB_C in GBHawk", + "window.arg.late_wy_FFto2_ly2_scx2_1_dmg08_cgb04c_out3 on DMG in GBHawk", + "window.arg.late_wy_FFto2_ly2_scx2_2_dmg08_out3_cgb04c_out0 on DMG in GBHawk", + "window.arg.late_wy_FFto2_ly2_scx3_1_dmg08_cgb04c_out3 on CGB_C in GBHawk", + "window.arg.late_wy_FFto2_ly2_scx3_1_dmg08_cgb04c_out3 on DMG in GBHawk", + "window.arg.late_wy_FFto2_ly2_scx3_2_dmg08_out3_cgb04c_out0 on DMG in GBHawk", + "window.arg.late_wy_FFto2_ly2_scx5_1_dmg08_cgb04c_out3 on CGB_C in GBHawk", + "window.arg.late_wy_FFto2_ly2_scx5_1_dmg08_cgb04c_out3 on DMG in GBHawk", + "window.arg.late_wy_FFto2_ly2_scx5_2_dmg08_out3_cgb04c_out0 on DMG in GBHawk", + "window.arg.late_wy_FFto2_ly2_scx5_ds_1_cgb04c_out3 on CGB_C in GBHawk", + "window.arg.late_wy_FFto2_ly2_wx00_1_dmg08_cgb04c_out3 on CGB_C in GBHawk", + "window.arg.late_wy_FFto2_ly2_wx00_1_dmg08_cgb04c_out3 on DMG in GBHawk", + "window.arg.late_wy_FFto2_ly2_wx00_2_dmg08_out3_cgb04c_out0 on DMG in GBHawk", + "window.arg.late_wy_FFto2_ly2_wx0f_1_dmg08_cgb04c_out3 on CGB_C in GBHawk", + "window.arg.late_wy_FFto2_ly2_wx0f_1_dmg08_cgb04c_out3 on DMG in GBHawk", + "window.arg.late_wy_FFto2_ly2_wx0f_2_dmg08_out3_cgb04c_out0 on DMG in GBHawk", + "window.late_disable_0_dmg08_cgb04c_out0 on CGB_C in GBHawk", + "window.late_disable_0_dmg08_cgb04c_out0 on DMG in GBHawk", + "window.late_disable_1_dmg08_out3_cgb04c_out0 on CGB_C in GBHawk", + "window.late_disable_ds_1_cgb04c_out0 on CGB_C in GBHawk", + "window.late_disable_early_scx00_wx0f_ds_1_cgb04c_out0 on CGB_C in GBHawk", + "window.late_disable_early_scx00_wx10_ds_1_cgb04c_out0 on CGB_C in GBHawk", + "window.late_disable_early_scx00_wx11_ds_1_cgb04c_out0 on CGB_C in GBHawk", + "window.late_disable_early_scx00_wx12_ds_1_cgb04c_out0 on CGB_C in GBHawk", + "window.late_disable_early_scx03_wx0f_1_dmg08_cgb04c_out0 on CGB_C in GBHawk", + "window.late_disable_early_scx03_wx0f_1_dmg08_cgb04c_out0 on DMG in GBHawk", + "window.late_disable_early_scx03_wx10_1_dmg08_cgb04c_out0 on CGB_C in GBHawk", + "window.late_disable_early_scx03_wx10_1_dmg08_cgb04c_out0 on DMG in GBHawk", + "window.late_disable_early_scx03_wx11_1_dmg08_cgb04c_out0 on CGB_C in GBHawk", + "window.late_disable_early_scx03_wx11_1_dmg08_cgb04c_out0 on DMG in GBHawk", + "window.late_disable_early_scx03_wx12_1_dmg08_cgb04c_out0 on CGB_C in GBHawk", + "window.late_disable_early_scx03_wx12_1_dmg08_cgb04c_out0 on DMG in GBHawk", + "window.late_disable_early_scx03_wx12_2_dmg08_out0_cgb04c_out3 on DMG in GBHawk", + "window.late_disable_late_scx00_wx0f_ds_1_cgb04c_out0 on CGB_C in GBHawk", + "window.late_disable_late_scx00_wx10_ds_1_cgb04c_out0 on CGB_C in GBHawk", + "window.late_disable_late_scx03_wx0f_1_dmg08_cgb04c_out0 on CGB_C in GBHawk", + "window.late_disable_late_scx03_wx0f_1_dmg08_cgb04c_out0 on DMG in GBHawk", + "window.late_disable_late_scx03_wx0f_2_dmg08_out3_cgb04c_out0 on CGB_C in GBHawk", + "window.late_disable_late_scx03_wx10_1_dmg08_cgb04c_out0 on CGB_C in GBHawk", + "window.late_disable_late_scx03_wx10_1_dmg08_cgb04c_out0 on DMG in GBHawk", + "window.late_disable_late_scx03_wx10_2_dmg08_out3_cgb04c_out0 on CGB_C in GBHawk", + "window.late_disable_late_scx03_wx11_1_dmg08_cgb04c_out0 on CGB_C in GBHawk", + "window.late_disable_late_scx03_wx11_1_dmg08_cgb04c_out0 on DMG in GBHawk", + "window.late_disable_late_scx03_wx11_2_dmg08_out3_cgb04c_out0 on CGB_C in GBHawk", + "window.late_disable_late_scx03_wx11_2_dmg08_out3_cgb04c_out0 on CGB_C in GBHawk", + "window.late_disable_late_scx03_wx12_1_dmg08_cgb04c_out0 on CGB_C in GBHawk", + "window.late_disable_late_scx03_wx12_1_dmg08_cgb04c_out0 on CGB_C in GBHawk", + "window.late_disable_late_scx03_wx12_1_dmg08_cgb04c_out0 on DMG in GBHawk", + "window.late_disable_late_scx03_wx12_1_dmg08_cgb04c_out0 on DMG in GBHawk", + "window.late_disable_scx2_0_dmg08_cgb04c_out0 on CGB_C in GBHawk", + "window.late_disable_scx2_0_dmg08_cgb04c_out0 on DMG in GBHawk", + "window.late_disable_scx2_1_dmg08_out3_cgb04c_out0 on CGB_C in GBHawk", + "window.late_disable_scx3_0_dmg08_cgb04c_out0 on CGB_C in GBHawk", + "window.late_disable_scx3_0_dmg08_cgb04c_out0 on DMG in GBHawk", + "window.late_disable_scx3_1_dmg08_out3_cgb04c_out0 on CGB_C in GBHawk", + "window.late_disable_scx5_0_dmg08_cgb04c_out0 on CGB_C in GBHawk", + "window.late_disable_scx5_0_dmg08_cgb04c_out0 on DMG in GBHawk", + "window.late_disable_scx5_1_dmg08_out3_cgb04c_out0 on CGB_C in GBHawk", + "window.late_disable_scx5_ds_1_cgb04c_out0 on CGB_C in GBHawk", + "window.late_disable_spx10_wx0f_1_dmg08_cgb04c_out0 on CGB_C in GBHawk", + "window.late_disable_spx10_wx0f_1_dmg08_cgb04c_out0 on DMG in GBHawk", + "window.late_disable_wx0f_0_dmg08_cgb04c_out0 on CGB_C in GBHawk", + "window.late_disable_wx0f_0_dmg08_cgb04c_out0 on DMG in GBHawk", + "window.late_disable_wx0f_1_dmg08_out3_cgb04c_out0 on CGB_C in GBHawk", + "window.late_enable_afterVblank_2_dmg08_out3_cgb04c_out0 on CGB_C in GBHawk", + "window.late_enable_afterVblank_3_dmg08_cgb04c_out0 on CGB_C in GBHawk", + "window.late_enable_afterVblank_3_dmg08_cgb04c_out0 on DMG in GBHawk", + "window.late_enable_afterVblank_ds_2_cgb04c_out0 on CGB_C in GBHawk", + "window.late_enable_afterVblank_ds_lcdoffset1_2_cgb04c_out0 on CGB_C in GBHawk", + "window.late_enable_afterVblank_lcdoffset1_2_cgb04c_out0 on CGB_C in GBHawk", + "window.late_enable_ly0_ds_1_cgb04c_out3 on CGB_C in GBHawk", + "window.late_reenable_1_dmg08_cgb04c_out3 on CGB_C in GBHawk", + "window.late_reenable_1_dmg08_cgb04c_out3 on DMG in GBHawk", + "window.late_reenable_ds_2_cgb04c_out0 on CGB_C in GBHawk", + "window.late_reenable_scx2_1_dmg08_cgb04c_out3 on CGB_C in GBHawk", + "window.late_reenable_scx2_1_dmg08_cgb04c_out3 on DMG in GBHawk", + "window.late_reenable_scx2_2_dmg08_out3_cgb04c_out0 on DMG in GBHawk", + "window.late_reenable_scx2_3_dmg08_cgb04c_out0 on CGB_C in GBHawk", + "window.late_reenable_scx2_3_dmg08_cgb04c_out0 on DMG in GBHawk", + "window.late_reenable_scx3_1_dmg08_cgb04c_out3 on CGB_C in GBHawk", + "window.late_reenable_scx3_1_dmg08_cgb04c_out3 on DMG in GBHawk", + "window.late_reenable_scx3_2_dmg08_out3_cgb04c_out0 on DMG in GBHawk", + "window.late_reenable_scx3_3_dmg08_cgb04c_out0 on CGB_C in GBHawk", + "window.late_reenable_scx3_3_dmg08_cgb04c_out0 on DMG in GBHawk", + "window.late_reenable_scx5_1_dmg08_cgb04c_out3 on CGB_C in GBHawk", + "window.late_reenable_scx5_1_dmg08_cgb04c_out3 on DMG in GBHawk", + "window.late_reenable_scx5_2_dmg08_out3_cgb04c_out0 on CGB_C in GBHawk", + "window.late_reenable_scx5_3_dmg08_cgb04c_out0 on CGB_C in GBHawk", + "window.late_reenable_scx5_3_dmg08_cgb04c_out0 on DMG in GBHawk", + "window.late_reenable_scx5_ds_2_cgb04c_out0 on CGB_C in GBHawk", + "window.late_reenable_wx0f_2_dmg08_cgb04c_out0 on CGB_C in GBHawk", + "window.late_reenable_wx0f_2_dmg08_cgb04c_out0 on DMG in GBHawk", + "window.late_scx_late_disable_0_dmg08_cgb04c_out0 on CGB_C in GBHawk", + "window.late_scx_late_disable_0_dmg08_cgb04c_out0 on DMG in GBHawk", + "window.late_scx_late_disable_1_dmg08_out3_cgb04c_out0 on CGB_C in GBHawk", + "window.late_wx_1_dmg08_cgb04c_out0 on CGB_C in GBHawk", + "window.late_wx_1_dmg08_cgb04c_out0 on DMG in GBHawk", + "window.late_wx_ds_1_cgb04c_out0 on CGB_C in GBHawk", + "window.late_wx_ff_07_1_dmg08_cgb04c_out3 on CGB_C in GBHawk", + "window.late_wx_ff_07_1_dmg08_cgb04c_out3 on DMG in GBHawk", + "window.late_wx_ff_0f_1_dmg08_cgb04c_out3 on CGB_C in GBHawk", + "window.late_wx_ff_0f_1_dmg08_cgb04c_out3 on DMG in GBHawk", + "window.late_wx_scx2_1_dmg08_cgb04c_out0 on CGB_C in GBHawk", + "window.late_wx_scx2_1_dmg08_cgb04c_out0 on DMG in GBHawk", + "window.late_wx_scx3_1_dmg08_cgb04c_out0 on CGB_C in GBHawk", + "window.late_wx_scx3_1_dmg08_cgb04c_out0 on DMG in GBHawk", + "window.late_wx_scx3_2_dmg08_out0_cgb04c_out3 on DMG in GBHawk", + "window.late_wx_scx5_1_dmg08_cgb04c_out0 on CGB_C in GBHawk", + "window.late_wx_scx5_1_dmg08_cgb04c_out0 on DMG in GBHawk", + "window.late_wx_scx5_ds_1_cgb04c_out0 on CGB_C in GBHawk", + "window.late_wx_wx03_1_dmg08_cgb04c_out0 on CGB_C in GBHawk", + "window.late_wx_wx03_1_dmg08_cgb04c_out0 on DMG in GBHawk", + "window.late_wx_wx0f_1_dmg08_cgb04c_out0 on CGB_C in GBHawk", + "window.late_wx_wx0f_1_dmg08_cgb04c_out0 on DMG in GBHawk", + "window.late_wy_2_dmg08_cgb04c_out3 on CGB_C in GBHawk", + "window.late_wy_2_dmg08_cgb04c_out3 on DMG in GBHawk", + "window.late_wy_ds_2_cgb04c_out3 on CGB_C in GBHawk", + "window.late_wy_ds_lcdoffset1_2_cgb04c_out3 on CGB_C in GBHawk", + "window.late_wy_lcdoffset1_2_cgb04c_out3 on CGB_C in GBHawk", + "window.m2int_wxA6_firstline_m3stat_2_dmg08_out0_cgb04c_out3 on DMG in GBHawk", + "window.m2int_wxA6_m0irq_2_dmg08_cgb04c_out2 on CGB_C in GBHawk", + "window.m2int_wxA6_m0irq_2_dmg08_cgb04c_out2 on DMG in GBHawk", + "window.m2int_wxA6_m0irq2_2_dmg08_cgb04c_out2 on CGB_C in GBHawk", + "window.m2int_wxA6_m0irq2_2_dmg08_cgb04c_out2 on DMG in GBHawk", + "window.m2int_wxA6_m3stat_2_dmg08_out0_cgb04c_out3 on DMG in GBHawk", + "window.m2int_wxA6_oambusyread_2_dmg08_out5_cgb04c_out0 on DMG in GBHawk", + "window.m2int_wxA6_scx2_m3stat_2_dmg08_out0_cgb04c_out3 on DMG in GBHawk", + "window.m2int_wxA6_scx2_m3stat_3_dmg08_out0_cgb04c_out3 on DMG in GBHawk", + "window.m2int_wxA6_scx3_m3stat_2_dmg08_out0_cgb04c_out3 on DMG in GBHawk", + "window.m2int_wxA6_scx3_m3stat_3_dmg08_out0_cgb04c_out3 on DMG in GBHawk", + "window.m2int_wxA6_scx5_m3stat_2_dmg08_out0_cgb04c_out3 on DMG in GBHawk", + "window.m2int_wxA6_spxA7_m0irq_2_dmg08_cgb04c_out2 on CGB_C in GBHawk", + "window.m2int_wxA6_spxA7_m0irq_2_dmg08_cgb04c_out2 on DMG in GBHawk", + "window.m2int_wxA6_spxA7_m3stat_2_dmg08_out0_cgb04c_out3 on DMG in GBHawk", + "window.m2int_wxA6_spxA7_m3stat_4_dmg08_out0_cgb04c_out3 on DMG in GBHawk", + "window.m2int_wxA6_vrambusyread_2_dmg08_out5_cgb04c_out0 on DMG in GBHawk", + }; + + public readonly string ExpectedValue; + + public readonly string RomEmbedPath; + + public readonly CoreSetup Setup; + + public readonly string TestName; + + public GambatteHexStrTestCase(string testName, CoreSetup setup, string romEmbedPath, string expectedValue) + { + TestName = testName; + Setup = setup; + RomEmbedPath = romEmbedPath; + ExpectedValue = expectedValue; + } + + public readonly string DisplayName() + => $"{TestName} on {Setup}"; + } + + public readonly struct GambatteRefImageTestCase + { + public static readonly GambatteRefImageTestCase Dummy = new("missing_files", new(CoreNames.Gambatte, ConsoleVariant.DMG), string.Empty, string.Empty); + + public static readonly IReadOnlyCollection KnownFailures = new[] + { + "bgtiledata.bgtiledata_spx08_1 on DMG in GBHawk", + "bgtiledata.bgtiledata_spx08_2 on DMG in GBHawk", + "bgtiledata.bgtiledata_spx08_3 on DMG in GBHawk", + "bgtiledata.bgtiledata_spx08_4 on DMG in GBHawk", + "bgtiledata.bgtiledata_spx0A_1 on DMG in GBHawk", + "bgtiledata.bgtiledata_spx0A_2 on DMG in GBHawk", + "bgtiledata.bgtiledata_spx0A_3 on DMG in GBHawk", + "bgtiledata.bgtiledata_spx0A_4 on DMG in GBHawk", + "bgtilemap.bgtilemap_spx08_ds_1 on CGB_C in GBHawk", + "bgtilemap.bgtilemap_spx08_ds_2 on CGB_C in GBHawk", + "bgtilemap.bgtilemap_spx08_ds_3 on CGB_C in GBHawk", + "bgtilemap.bgtilemap_spx08_ds_4 on CGB_C in GBHawk", + "bgtilemap.bgtilemap_spx09_1 on CGB_C in GBHawk", + "bgtilemap.bgtilemap_spx09_2 on CGB_C in GBHawk", + "bgtilemap.bgtilemap_spx09_3 on CGB_C in GBHawk", + "bgtilemap.bgtilemap_spx09_4 on CGB_C in GBHawk", + "bgtilemap.bgtilemap_spx0A_1 on DMG in GBHawk", + "bgtilemap.bgtilemap_spx0A_2 on DMG in GBHawk", + "bgtilemap.bgtilemap_spx0A_3 on DMG in GBHawk", + "bgtilemap.bgtilemap_spx0A_4 on DMG in GBHawk", + "dmgpalette_during_m3.dmgpalette_during_m3_2 on DMG in GBHawk", + "dmgpalette_during_m3.dmgpalette_during_m3_3 on DMG in GBHawk", + "dmgpalette_during_m3.dmgpalette_during_m3_4 on DMG in GBHawk", + "dmgpalette_during_m3.dmgpalette_during_m3_5 on DMG in GBHawk", + "dmgpalette_during_m3.dmgpalette_during_m3_scx1_4 on DMG in GBHawk", + "dmgpalette_during_m3.dmgpalette_during_m3_scx2_1 on DMG in GBHawk", + "dmgpalette_during_m3.lycint_dmgpalette_during_m3_1 on DMG in GBHawk", + "dmgpalette_during_m3.lycint_dmgpalette_during_m3_2 on DMG in GBHawk", + "dmgpalette_during_m3.lycint_dmgpalette_during_m3_3 on DMG in GBHawk", + "dmgpalette_during_m3.lycint_dmgpalette_during_m3_4 on DMG in GBHawk", + "dmgpalette_during_m3.scx3.dmgpalette_during_m3_1 on DMG in GBHawk", + "dmgpalette_during_m3.scx3.dmgpalette_during_m3_2 on DMG in GBHawk", + "dmgpalette_during_m3.scx3.dmgpalette_during_m3_3 on DMG in GBHawk", + "dmgpalette_during_m3.scx3.dmgpalette_during_m3_4 on DMG in GBHawk", + "dmgpalette_during_m3.scx3.dmgpalette_during_m3_5 on DMG in GBHawk", + "scx_during_m3.scx_0063c0.scx_during_m3_1 on CGB_C in GBHawk", + "scx_during_m3.scx_0063c0.scx_during_m3_1 on DMG in GBHawk", + "scx_during_m3.scx_0063c0.scx_during_m3_2 on CGB_C in GBHawk", + "scx_during_m3.scx_0063c0.scx_during_m3_2 on DMG in GBHawk", + "scx_during_m3.scx_0063c0.scx_during_m3_ds_1 on CGB_C in GBHawk", + "scx_during_m3.scx_0063c0.scx_during_m3_ds_2 on CGB_C in GBHawk", + "scx_during_m3.scx_0360c0.scx_during_m3_2 on CGB_C in GBHawk", + "scx_during_m3.scx_0360c0.scx_during_m3_2 on DMG in GBHawk", + "scx_during_m3.scx_0360c0.scx_during_m3_3 on CGB_C in GBHawk", + "scx_during_m3.scx_0360c0.scx_during_m3_3 on DMG in GBHawk", + "scx_during_m3.scx_0360c0.scx_during_m3_4 on CGB_C in GBHawk", + "scx_during_m3.scx_0360c0.scx_during_m3_4 on DMG in GBHawk", + "scx_during_m3.scx_0360c0.scx_during_m3_5 on CGB_C in GBHawk", + "scx_during_m3.scx_0360c0.scx_during_m3_5 on DMG in GBHawk", + "scx_during_m3.scx_0360c0.scx_during_m3_6 on CGB_C in GBHawk", + "scx_during_m3.scx_0360c0.scx_during_m3_6 on DMG in GBHawk", + "scx_during_m3.scx_0360c0.scx_during_m3_ds_1 on CGB_C in GBHawk", + "scx_during_m3.scx_0360c0.scx_during_m3_ds_2 on CGB_C in GBHawk", + "scx_during_m3.scx_0360c0.scx_during_m3_ds_3 on CGB_C in GBHawk", + "scx_during_m3.scx_0360c0.scx_during_m3_ds_4 on CGB_C in GBHawk", + "scx_during_m3.scx_0360c0.scx_during_m3_ds_5 on CGB_C in GBHawk", + "scx_during_m3.scx_0360c0.scx_during_m3_ds_6 on CGB_C in GBHawk", + "scx_during_m3.scx_0360c0.scx_during_m3_ds_7 on CGB_C in GBHawk", + "scx_during_m3.scx_0360c0.scx_during_m3_ds_8 on CGB_C in GBHawk", + "scx_during_m3.scx_0363c0.scx_during_m3_1 on CGB_C in GBHawk", + "scx_during_m3.scx_0363c0.scx_during_m3_1 on DMG in GBHawk", + "scx_during_m3.scx_0363c0.scx_during_m3_2 on CGB_C in GBHawk", + "scx_during_m3.scx_0363c0.scx_during_m3_2 on DMG in GBHawk", + "scx_during_m3.scx_0363c0.scx_during_m3_3 on CGB_C in GBHawk", + "scx_during_m3.scx_0363c0.scx_during_m3_3 on DMG in GBHawk", + "scx_during_m3.scx_0363c0.scx_during_m3_4 on CGB_C in GBHawk", + "scx_during_m3.scx_0363c0.scx_during_m3_4 on DMG in GBHawk", + "scx_during_m3.scx_0363c0.scx_during_m3_5 on CGB_C in GBHawk", + "scx_during_m3.scx_0363c0.scx_during_m3_5 on DMG in GBHawk", + "scx_during_m3.scx_0363c0.scx_during_m3_6 on CGB_C in GBHawk", + "scx_during_m3.scx_0363c0.scx_during_m3_6 on DMG in GBHawk", + "scx_during_m3.scx_0363c0.scx_during_m3_ds_1 on CGB_C in GBHawk", + "scx_during_m3.scx_0363c0.scx_during_m3_ds_2 on CGB_C in GBHawk", + "scx_during_m3.scx_0363c0.scx_during_m3_ds_3 on CGB_C in GBHawk", + "scx_during_m3.scx_0363c0.scx_during_m3_ds_4 on CGB_C in GBHawk", + "scx_during_m3.scx_0363c0.scx_during_m3_ds_5 on CGB_C in GBHawk", + "scx_during_m3.scx_0363c0.scx_during_m3_ds_6 on CGB_C in GBHawk", + "scx_during_m3.scx_0363c0.scx_during_m3_ds_7 on CGB_C in GBHawk", + "scx_during_m3.scx_0363c0.scx_during_m3_ds_8 on CGB_C in GBHawk", + "scx_during_m3.scx_0367c0.scx_during_m3_1 on CGB_C in GBHawk", + "scx_during_m3.scx_0367c0.scx_during_m3_1 on DMG in GBHawk", + "scx_during_m3.scx_0367c0.scx_during_m3_2 on CGB_C in GBHawk", + "scx_during_m3.scx_0367c0.scx_during_m3_2 on DMG in GBHawk", + "scx_during_m3.scx_0367c0.scx_during_m3_3 on CGB_C in GBHawk", + "scx_during_m3.scx_0367c0.scx_during_m3_3 on DMG in GBHawk", + "scx_during_m3.scx_0367c0.scx_during_m3_4 on CGB_C in GBHawk", + "scx_during_m3.scx_0367c0.scx_during_m3_4 on DMG in GBHawk", + "scx_during_m3.scx_0367c0.scx_during_m3_5 on CGB_C in GBHawk", + "scx_during_m3.scx_0367c0.scx_during_m3_5 on DMG in GBHawk", + "scx_during_m3.scx_0367c0.scx_during_m3_6 on CGB_C in GBHawk", + "scx_during_m3.scx_0367c0.scx_during_m3_6 on DMG in GBHawk", + "scx_during_m3.scx_0367c0.scx_during_m3_ds_1 on CGB_C in GBHawk", + "scx_during_m3.scx_0367c0.scx_during_m3_ds_2 on CGB_C in GBHawk", + "scx_during_m3.scx_0367c0.scx_during_m3_ds_3 on CGB_C in GBHawk", + "scx_during_m3.scx_0367c0.scx_during_m3_ds_4 on CGB_C in GBHawk", + "scx_during_m3.scx_0367c0.scx_during_m3_ds_5 on CGB_C in GBHawk", + "scx_during_m3.scx_0367c0.scx_during_m3_ds_6 on CGB_C in GBHawk", + "scx_during_m3.scx_0367c0.scx_during_m3_ds_7 on CGB_C in GBHawk", + "scx_during_m3.scx_0367c0.scx_during_m3_ds_8 on CGB_C in GBHawk", + "scx_during_m3.scx_0761c0.scx_during_m3_1 on CGB_C in GBHawk", + "scx_during_m3.scx_0761c0.scx_during_m3_2 on CGB_C in GBHawk", + "scx_during_m3.scx_0761c0.scx_during_m3_2 on DMG in GBHawk", + "scx_during_m3.scx_0761c0.scx_during_m3_3 on CGB_C in GBHawk", + "scx_during_m3.scx_0761c0.scx_during_m3_3 on DMG in GBHawk", + "scx_during_m3.scx_0761c0.scx_during_m3_4 on CGB_C in GBHawk", + "scx_during_m3.scx_0761c0.scx_during_m3_4 on DMG in GBHawk", + "scx_during_m3.scx_0761c0.scx_during_m3_5 on CGB_C in GBHawk", + "scx_during_m3.scx_0761c0.scx_during_m3_5 on DMG in GBHawk", + "scx_during_m3.scx_0761c0.scx_during_m3_6 on CGB_C in GBHawk", + "scx_during_m3.scx_0761c0.scx_during_m3_6 on DMG in GBHawk", + "scx_during_m3.scx_0761c0.scx_during_m3_ds_1 on CGB_C in GBHawk", + "scx_during_m3.scx_0761c0.scx_during_m3_ds_2 on CGB_C in GBHawk", + "scx_during_m3.scx_0761c0.scx_during_m3_ds_3 on CGB_C in GBHawk", + "scx_during_m3.scx_0761c0.scx_during_m3_ds_4 on CGB_C in GBHawk", + "scx_during_m3.scx_0761c0.scx_during_m3_ds_5 on CGB_C in GBHawk", + "scx_during_m3.scx_0761c0.scx_during_m3_ds_6 on CGB_C in GBHawk", + "scx_during_m3.scx_0761c0.scx_during_m3_ds_7 on CGB_C in GBHawk", + "scx_during_m3.scx_0761c0.scx_during_m3_ds_8 on CGB_C in GBHawk", + "scx_during_m3.scx_attrib_during_m3_spx1_ds on CGB_C in GBHawk", + "scx_during_m3.scx_attrib_during_m3_spx2_ds on CGB_C in GBHawk", + "scx_during_m3.scx_during_m3_spx0 on CGB_C in GBHawk", + "scx_during_m3.scx_during_m3_spx0 on DMG in GBHawk", + "scx_during_m3.scx_during_m3_spx1 on CGB_C in GBHawk", + "scx_during_m3.scx_during_m3_spx1 on DMG in GBHawk", + "scx_during_m3.scx_during_m3_spx2 on CGB_C in GBHawk", + "scx_during_m3.scx_during_m3_spx2 on DMG in GBHawk", + "scx_during_m3.scx_during_m3_spx2_ds on CGB_C in GBHawk", + "scx_during_m3.scx1_scx0_during_m3_1 on CGB_C in GBHawk", + "scx_during_m3.scx2_scx0_during_m3_1 on CGB_C in GBHawk", + "scx_during_m3.scx2_scx0_during_m3_1 on DMG in GBHawk", + "scx_during_m3.scx2_scx1_during_m3_1 on CGB_C in GBHawk", + "scy.scx3.scy_during_m3_2 on CGB_C in GBHawk", + "scy.scx3.scy_during_m3_4 on CGB_C in GBHawk", + "scy.scx3.scy_during_m3_6 on CGB_C in GBHawk", + "scy.scy_during_m3_2 on CGB_C in GBHawk", + "scy.scy_during_m3_4 on CGB_C in GBHawk", + "scy.scy_during_m3_6 on CGB_C in GBHawk", + "scy.scy_during_m3_spx08_1 on CGB_C in GBHawk", + "scy.scy_during_m3_spx08_2 on DMG in GBHawk", + "scy.scy_during_m3_spx08_3 on CGB_C in GBHawk", + "scy.scy_during_m3_spx08_ds_1 on CGB_C in GBHawk", + "scy.scy_during_m3_spx08_ds_2 on CGB_C in GBHawk", + "scy.scy_during_m3_spx08_ds_3 on CGB_C in GBHawk", + "scy.scy_during_m3_spx08_ds_4 on CGB_C in GBHawk", + "scy.scy_during_m3_spx09_1 on CGB_C in GBHawk", + "scy.scy_during_m3_spx09_2 on CGB_C in GBHawk", + "scy.scy_during_m3_spx09_3 on CGB_C in GBHawk", + "scy.scy_during_m3_spx09_4 on CGB_C in GBHawk", + "scy.scy_during_m3_spx0A_1 on CGB_C in GBHawk", + "scy.scy_during_m3_spx0A_1 on DMG in GBHawk", + "scy.scy_during_m3_spx0A_2 on DMG in GBHawk", + "scy.scy_during_m3_spx0A_3 on CGB_C in GBHawk", + "scy.scy_during_m3_spx0A_3 on DMG in GBHawk", + "scy.scy_during_m3_spx0B_1 on CGB_C in GBHawk", + "scy.scy_during_m3_spx0B_3 on CGB_C in GBHawk", + "window.on_screen.weon_wx18_weoff_weon_wx80 on CGB_C in GBHawk", + "window.on_screen.weon_wx18_weoff_weon_wx80 on DMG in GBHawk", + "window.on_screen.wx17_weoff_wxA5_weon on CGB_C in GBHawk", + "window.on_screen.wx17_weoff_wxA5_weon on DMG in GBHawk", + "window.on_screen.wxA5_weoff_at_xposA5 on CGB_C in GBHawk", + "window.on_screen.wxA5_weoff_at_xposA5 on DMG in GBHawk", + "window.on_screen.wxA6_3 on DMG in GBHawk", + "window.on_screen.wxA6_late_we_reenable_1 on DMG in GBHawk", + "window.on_screen.wxA6_late_we_reenable_2 on CGB_C in GBHawk", + "window.on_screen.wxA6_late_we_reenable_2 on DMG in GBHawk", + "window.on_screen.wxA6_late_we_reenable_3 on CGB_C in GBHawk", + "window.on_screen.wxA6_late_we_reenable_3 on DMG in GBHawk", + "window.on_screen.wxA6_late_we_reenable_4 on CGB_C in GBHawk", + "window.on_screen.wxA6_scx7 on DMG in GBHawk", + "window.on_screen.wxA6_weoff_at_xposA6 on CGB_C in GBHawk", + "window.on_screen.wxA6_weoff_at_xposA6 on DMG in GBHawk", + "window.on_screen.wxA6_wy00 on DMG in GBHawk", + "window.on_screen.wxA6_wy01 on DMG in GBHawk", + "window.on_screen.wxA6_wy01_weoff_ly02 on DMG in GBHawk", + "window.on_screen.wxA6_wy01_weoff_ly02_weon_ly60 on DMG in GBHawk", + "window.on_screen.wxA6_wy01_wxA5_ly02 on DMG in GBHawk", + "window.on_screen.wxA6_wy01_wxA7_ly02 on DMG in GBHawk", + "window.on_screen.wxA6_wy8F on DMG in GBHawk", + }; + + public readonly string ExpectEmbedPath; + + public readonly string RomEmbedPath; + + public readonly CoreSetup Setup; + + public readonly string TestName; + + public GambatteRefImageTestCase(string testName, CoreSetup setup, string romEmbedPath, string expectEmbedPath) + { + TestName = testName; + Setup = setup; + RomEmbedPath = romEmbedPath; + ExpectEmbedPath = expectEmbedPath; + } + + public readonly string DisplayName() + => $"{TestName} on {Setup}"; + } + } +} diff --git a/src/BizHawk.Tests.Testroms.GB.GambatteSuite/GambatteSuite.cs b/src/BizHawk.Tests.Testroms.GB.GambatteSuite/GambatteSuite.cs new file mode 100644 index 0000000000..884f0ca03b --- /dev/null +++ b/src/BizHawk.Tests.Testroms.GB.GambatteSuite/GambatteSuite.cs @@ -0,0 +1,226 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Globalization; +using System.Linq; +using System.Reflection; +using System.Text.RegularExpressions; + +using BizHawk.Common.CollectionExtensions; +using BizHawk.Common.IOExtensions; +using BizHawk.Common.StringExtensions; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using static BizHawk.Tests.Testroms.GB.GBHelper; + +namespace BizHawk.Tests.Testroms.GB.GambatteSuite +{ + [TestClass] + public sealed partial class GambatteSuite + { + /// there are 4664 * 3 cores = 13992 of these tests @_0 + [AttributeUsage(AttributeTargets.Method)] + private sealed class GambatteHexStrTestDataAttribute : Attribute, ITestDataSource + { + public IEnumerable GetData(MethodInfo methodInfo) + { + if (!RomsArePresent) return new[] { new object?[] { GambatteHexStrTestCase.Dummy } }; + return (_allCases ??= EnumerateAllCases()).HexStrCases +// .Where(static testCase => testCase.Setup.Variant is ConsoleVariant.DMG) // uncomment and modify to run a subset of the test cases... + .Where(static testCase => !TestUtils.ShouldIgnoreCase(SUITE_ID, testCase.DisplayName())) // ...or use the global blocklist in TestUtils + .OrderBy(static testCase => testCase.DisplayName()) + .Select(static testCase => new object?[] { testCase }); + } + + public string GetDisplayName(MethodInfo methodInfo, object?[] data) + => $"{methodInfo.Name}({((GambatteHexStrTestCase) data[0]!).DisplayName()})"; + } + + [AttributeUsage(AttributeTargets.Method)] + private sealed class GambatteRefImageTestDataAttribute : Attribute, ITestDataSource + { + public IEnumerable GetData(MethodInfo methodInfo) + { + if (!RomsArePresent) return new[] { new object?[] { GambatteRefImageTestCase.Dummy } }; + return (_allCases ??= EnumerateAllCases()).RefImageCases +// .Where(static testCase => testCase.Setup.Variant is ConsoleVariant.DMG) // uncomment and modify to run a subset of the test cases... + .Where(static testCase => !TestUtils.ShouldIgnoreCase(SUITE_ID, testCase.DisplayName())) // ...or use the global blocklist in TestUtils + .OrderBy(static testCase => testCase.DisplayName()) + .Select(static testCase => new object?[] { testCase }); + } + + public string GetDisplayName(MethodInfo methodInfo, object?[] data) + => $"{methodInfo.Name}({((GambatteRefImageTestCase) data[0]!).DisplayName()})"; + } + + private static readonly byte[,] GLYPHS = { + { 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000 }, + { 0b01111111, 0b00001000, 0b01111111, 0b01111111, 0b01000001, 0b01111111, 0b01111111, 0b01111111, 0b00111110, 0b01111111, 0b00001000, 0b01111110, 0b00111110, 0b01111110, 0b01111111, 0b01111111 }, + { 0b01000001, 0b00001000, 0b00000001, 0b00000001, 0b01000001, 0b01000000, 0b01000000, 0b00000001, 0b01000001, 0b01000001, 0b00100010, 0b01000001, 0b01000001, 0b01000001, 0b01000000, 0b01000000 }, + { 0b01000001, 0b00001000, 0b00000001, 0b00000001, 0b01000001, 0b01000000, 0b01000000, 0b00000010, 0b01000001, 0b01000001, 0b01000001, 0b01000001, 0b01000000, 0b01000001, 0b01000000, 0b01000000 }, + { 0b01000001, 0b00001000, 0b01111111, 0b00111111, 0b01111111, 0b01111110, 0b01111111, 0b00000100, 0b00111110, 0b01111111, 0b01111111, 0b01111110, 0b01000000, 0b01000001, 0b01111111, 0b01111111 }, + { 0b01000001, 0b00001000, 0b01000000, 0b00000001, 0b00000001, 0b00000001, 0b01000001, 0b00001000, 0b01000001, 0b00000001, 0b01000001, 0b01000001, 0b01000000, 0b01000001, 0b01000000, 0b01000000 }, + { 0b01000001, 0b00001000, 0b01000000, 0b00000001, 0b00000001, 0b00000001, 0b01000001, 0b00010000, 0b01000001, 0b00000001, 0b01000001, 0b01000001, 0b01000001, 0b01000001, 0b01000000, 0b01000000 }, + { 0b01111111, 0b00001000, 0b01111111, 0b01111111, 0b00000001, 0b01111110, 0b01111111, 0b00010000, 0b00111110, 0b01111111, 0b01000001, 0b01111110, 0b00111110, 0b01111110, 0b01111111, 0b01000000 }, + }; + + private const string SUITE_ID = "GambatteSuite"; + + private const string SUITE_PREFIX = "res.Gambatte_testroms_artifact."; + + private static (IReadOnlyList RefImageCases, IReadOnlyList HexStrCases)? _allCases = null; + + private static readonly Regex HexStrFilenameRegex = new(@"_out[0-9A-F]+\.gbc?$"); + + private static readonly bool RomsArePresent = ReflectionCache.EmbeddedResourceList().Any(static s => s.StartsWith(SUITE_PREFIX)); + + [ClassCleanup] + public static void AfterAll() + => TestUtils.WriteMetricsToDisk(); + + [ClassInitialize] + public static void BeforeAll(TestContext ctx) + => TestUtils.PrepareDBAndOutput(SUITE_ID); + + private static (IReadOnlyList RefImageCases, IReadOnlyList HexStrCases) EnumerateAllCases() + { + var variants = new[] { ("_cgb04c.png", ConsoleVariant.CGB_C), ("_dmg08.png", ConsoleVariant.DMG) }; + static IReadOnlyList<(ConsoleVariant Variant, string ExpectValue)> ParseHexStrFilename(string filename) + { + List<(ConsoleVariant Variant, string Expect)> parsed = new(); + string? lastSeenValue = null; + var endIndex = filename.LastIndexOf('.'); + while (true) + { + var i = filename.LastIndexOf('_', endIndex - 1); + var seg = filename[(i + 1)..endIndex]; + if (seg == "cgb04c") parsed.Add((ConsoleVariant.CGB_C, lastSeenValue!)); + else if (seg == "dmg08") parsed.Add((ConsoleVariant.DMG, lastSeenValue!)); + else if (seg.StartsWith("out")) lastSeenValue = seg.SubstringAfter("out"); + else return parsed; + endIndex = i; + } + } + var allFilenames = ReflectionCache.EmbeddedResourceList(SUITE_PREFIX).ToList(); + List refImageCases = new(); + foreach (var filename in allFilenames.Where(static item => item.EndsWith(".png")).ToList()) + { + var found = variants.FirstOrNull(kvp => filename.EndsWith(kvp.Item1)); + if (found is null) continue; + var (suffix, variant) = found.Value; + var testName = filename.RemoveSuffix(suffix); + var romEmbedPath = $"{testName}.{(testName.StartsWith("dmgpalette_during_m3") ? "gb" : "gbc")}"; + foreach (var setup in CoreSetup.ValidSetupsFor(variant)) + { + refImageCases.Add(new(testName, setup, SUITE_PREFIX + romEmbedPath, SUITE_PREFIX + filename)); + } + allFilenames.Remove(filename); + allFilenames.Remove(romEmbedPath); + } + var hexStrFilenames = allFilenames.Where(static s => HexStrFilenameRegex.IsMatch(s)).ToList(); + List hexStrCases = new(); + foreach (var hexStrFilename in hexStrFilenames) + { + var testName = hexStrFilename.SubstringBeforeLast('.'); + foreach (var (variant, expectValue) in ParseHexStrFilename(hexStrFilename)) foreach (var setup in CoreSetup.ValidSetupsFor(variant)) + { + hexStrCases.Add(new(testName, setup, SUITE_PREFIX + hexStrFilename, expectValue)); + } + allFilenames.Remove(hexStrFilename); + } +// Console.WriteLine($"unused files:\n>>>\n{string.Join("\n", allFilenames.OrderBy(static s => s))}\n<<<"); + return (refImageCases, hexStrCases); + } + + [DataTestMethod] + [GambatteHexStrTestData] + public void RunGambatteHexStrTest(GambatteHexStrTestCase testCase) + { + static bool GlyphMatches(Bitmap b, int xOffset, byte v) + { + // `(x, 0)` is the top-left of an 8x8 square of pixels to read from `b`, which is compared against the glyph for the nybble `v` + bool GlyphRowMatches(int y) + { + byte rowAsByte = 0; + for (int x = xOffset, l = x + 8; x < l; x++) + { + rowAsByte <<= 1; + if ((b.GetPixel(x, y).ToArgb() & 0xFFFFFF) == 0) rowAsByte |= 1; + } + return rowAsByte == GLYPHS[y, v]; + } + for (var y = 0; y < 8; y++) if (!GlyphRowMatches(y)) return false; + return true; + } + TestUtils.ShortCircuitMissingRom(RomsArePresent); + var caseStr = testCase.DisplayName(); + TestUtils.ShortCircuitKnownFailure(caseStr, GambatteHexStrTestCase.KnownFailures, out var knownFail); + var actualUnnormalised = DummyFrontend.RunAndScreenshot( + InitGBCore(testCase.Setup, testCase.RomEmbedPath.SubstringBeforeLast('.'), ReflectionCache.EmbeddedResourceStream(testCase.RomEmbedPath).ReadAllBytes()), + static fe => fe.FrameAdvanceBy(11)).AsBitmap(); + var glyphCount = testCase.ExpectedValue.Length; + var screenshotMatches = true; + var i = 0; + var xOffset = 0; + while (i < glyphCount) + { + if (!GlyphMatches(actualUnnormalised, xOffset, byte.Parse(testCase.ExpectedValue[i..(i + 1)], NumberStyles.HexNumber))) + { + screenshotMatches = false; + break; + } + i++; + xOffset += 8; + } + var state = TestUtils.SuccessState(screenshotMatches, knownFail); + if (!ImageUtils.SkipFileIO(state)) + { + ImageUtils.SaveScreenshot(NormaliseGBScreenshot(actualUnnormalised, testCase.Setup), (SUITE_ID, caseStr)); + Console.WriteLine($"should read: {testCase.ExpectedValue}"); + } + switch (state) + { + case TestUtils.TestSuccessState.ExpectedFailure: + Assert.Inconclusive("expected failure, verified"); + break; + case TestUtils.TestSuccessState.Failure: + Assert.Fail("screenshot contains incorrect value"); + break; + case TestUtils.TestSuccessState.UnexpectedSuccess: + Assert.Fail("screenshot contains correct value unexpectedly (this is a good thing)"); + break; + } + } + + [DataTestMethod] + [GambatteRefImageTestData] + public void RunGambatteRefImageTest(GambatteRefImageTestCase testCase) + { + TestUtils.ShortCircuitMissingRom(RomsArePresent); + var caseStr = testCase.DisplayName(); + TestUtils.ShortCircuitKnownFailure(caseStr, GambatteRefImageTestCase.KnownFailures, out var knownFail); + var actualUnnormalised = DummyFrontend.RunAndScreenshot( + InitGBCore(testCase.Setup, testCase.RomEmbedPath.SubstringBeforeLast('.'), ReflectionCache.EmbeddedResourceStream(testCase.RomEmbedPath).ReadAllBytes()), + static fe => fe.FrameAdvanceBy(14)); + var state = GBScreenshotsEqual( + ReflectionCache.EmbeddedResourceStream(testCase.ExpectEmbedPath), + actualUnnormalised, + knownFail, + testCase.Setup, + (SUITE_ID, caseStr)); + switch (state) + { + case TestUtils.TestSuccessState.ExpectedFailure: + Assert.Inconclusive("expected failure, verified"); + break; + case TestUtils.TestSuccessState.Failure: + Assert.Fail("expected and actual screenshots differ"); + break; + case TestUtils.TestSuccessState.UnexpectedSuccess: + Assert.Fail("expected and actual screenshots matched unexpectedly (this is a good thing)"); + break; + } + } + } +} diff --git a/src/BizHawk.Tests.Testroms.GB.GambatteSuite/Properties/AssemblyInfo.cs b/src/BizHawk.Tests.Testroms.GB.GambatteSuite/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..025cb0d716 --- /dev/null +++ b/src/BizHawk.Tests.Testroms.GB.GambatteSuite/Properties/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: TestDataSourceDiscovery(TestDataSourceDiscoveryOption.DuringExecution)] diff --git a/src/BizHawk.Tests.Testroms.GB.GambatteSuite/readme.md b/src/BizHawk.Tests.Testroms.GB.GambatteSuite/readme.md new file mode 100644 index 0000000000..ec18083048 --- /dev/null +++ b/src/BizHawk.Tests.Testroms.GB.GambatteSuite/readme.md @@ -0,0 +1,15 @@ +See the readme in the main project, `../BizHawk.Tests.Testroms.GB`. + +On Linux, run `/res/download_from_ci.sh` to automatically download and extract the CI artifacts containing the necessary testroms. +On Windows, run the same script in WSL, or do it manually (because Yoshi can't be bothered porting the script to PowerShell). +For this project, the expected directory structure is: +``` +res +└─ Gambatte-testroms_artifact +``` + +Note that firmware does not need to be copied here. They are taken from `../BizHawk.Tests.Testroms.GB/res/fw` if present. + +> This test suite is huge and takes a **really long time** to run. Like several hours. + +Summary of `BIZHAWKTEST_RUN_KNOWN_FAILURES=1 ./run_tests_release.sh` should read 13681 passed / 1304 skipped / 0 failed. diff --git a/src/BizHawk.Tests.Testroms.GB.GambatteSuite/res/download_from_ci.sh b/src/BizHawk.Tests.Testroms.GB.GambatteSuite/res/download_from_ci.sh new file mode 100755 index 0000000000..3885c9889c --- /dev/null +++ b/src/BizHawk.Tests.Testroms.GB.GambatteSuite/res/download_from_ci.sh @@ -0,0 +1,4 @@ +#!/bin/sh +set -e +cd "$(dirname "$(realpath "$0")")" +../../BizHawk.Tests.Testroms.GB/.download_from_ci.sh Gambatte-testroms diff --git a/src/BizHawk.Tests.Testroms.GB.GambatteSuite/run_tests_debug.sh b/src/BizHawk.Tests.Testroms.GB.GambatteSuite/run_tests_debug.sh new file mode 100755 index 0000000000..5ec4149a1f --- /dev/null +++ b/src/BizHawk.Tests.Testroms.GB.GambatteSuite/run_tests_debug.sh @@ -0,0 +1,2 @@ +#!/bin/sh +cd "$(dirname "$(realpath "$0")")" && ../BizHawk.Tests.Testroms.GB/.run_tests_with_configuration.sh "Debug" "$@" diff --git a/src/BizHawk.Tests.Testroms.GB.GambatteSuite/run_tests_release.sh b/src/BizHawk.Tests.Testroms.GB.GambatteSuite/run_tests_release.sh new file mode 100755 index 0000000000..be16859693 --- /dev/null +++ b/src/BizHawk.Tests.Testroms.GB.GambatteSuite/run_tests_release.sh @@ -0,0 +1,2 @@ +#!/bin/sh +cd "$(dirname "$(realpath "$0")")" && ../BizHawk.Tests.Testroms.GB/.run_tests_with_configuration.sh "Release" "$@" diff --git a/src/BizHawk.Tests.Testroms.GB/.download_from_ci.sh b/src/BizHawk.Tests.Testroms.GB/.download_from_ci.sh new file mode 100755 index 0000000000..782519d066 --- /dev/null +++ b/src/BizHawk.Tests.Testroms.GB/.download_from_ci.sh @@ -0,0 +1,23 @@ +#!/bin/sh +set -e +for j in "$@"; do + if [ -e "${j}_artifact" ]; then + printf "Using existing copy of %s\n" "$j" + else + curl -L -o "$j.zip" "https://gitlab.com/tasbot/libre-roms-ci/-/jobs/artifacts/master/download?job=$j" + unzip "$j.zip" >/dev/null + find "${j}_artifact" -type d -exec chmod 755 "{}" \; + find "${j}_artifact" -type f -exec chmod 644 "{}" \; + rm "$j.zip" + printf "Downloaded and extracted %s CI artifact\n" "$j" + fi +done +exit 0 + +# TODO finish this and put it in a separate script +nixVersion="$(nix --version 2>&1)" +if [ $? -eq 0 ]; then + for a in blargg-gb-tests; do + printf "(TODO: nix-build %s)\n" "$a" + done +fi diff --git a/src/BizHawk.Tests.Testroms.GB/.run_tests_with_configuration.sh b/src/BizHawk.Tests.Testroms.GB/.run_tests_with_configuration.sh new file mode 100755 index 0000000000..65116168d7 --- /dev/null +++ b/src/BizHawk.Tests.Testroms.GB/.run_tests_with_configuration.sh @@ -0,0 +1,8 @@ +#!/bin/sh +set -e +root="$(realpath "$PWD/../..")" +config="$1" +shift +res/download_from_ci.sh +export LD_LIBRARY_PATH="$root/output/dll:$LD_LIBRARY_PATH" +dotnet test -a "$root/test_output" -c "$config" -l "junit;LogFilePath=$root/test_output/{assembly}.coverage.xml;MethodFormat=Class;FailureBodyFormat=Verbose" -l "console;verbosity=detailed" "$@" diff --git a/src/BizHawk.Tests.Testroms.GB/BizHawk.Tests.Testroms.GB.csproj b/src/BizHawk.Tests.Testroms.GB/BizHawk.Tests.Testroms.GB.csproj new file mode 100644 index 0000000000..2fccf3b6b1 --- /dev/null +++ b/src/BizHawk.Tests.Testroms.GB/BizHawk.Tests.Testroms.GB.csproj @@ -0,0 +1,29 @@ + + + net6.0 + + + + $(ProjectDir)../../test_output + + + $(DefineConstants);SKIP_KNOWN_FAILURES + + + $(DefineConstants);SAVE_IMAGES_ON_FAIL + + + $(DefineConstants);SAVE_IMAGES_ON_FAIL;SAVE_IMAGES_ON_PASS + + + + + + + + + + + + + diff --git a/src/BizHawk.Tests.Testroms.GB/DummyFrontend.cs b/src/BizHawk.Tests.Testroms.GB/DummyFrontend.cs new file mode 100644 index 0000000000..ad71c97fa9 --- /dev/null +++ b/src/BizHawk.Tests.Testroms.GB/DummyFrontend.cs @@ -0,0 +1,242 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Linq; + +using BizHawk.Bizware.BizwareGL; +using BizHawk.Client.Common; +using BizHawk.Common.IOExtensions; +using BizHawk.Emulation.Common; + +namespace BizHawk.Tests.Testroms.GB +{ + public sealed class DummyFrontend : IDisposable + { + public sealed class EmbeddedFirmwareProvider : ICoreFileProvider + { + private static string FailMsg(string embedPath, string? msg) + => $"failed to open required resource at {embedPath}, is it present in $(ProjectDir)/res?{(msg is not null ? " core says: " + msg : string.Empty)}"; + + public readonly IDictionary EmbedPathMap; + + public EmbeddedFirmwareProvider(IDictionary? embedPathMap = null) + => EmbedPathMap = embedPathMap ?? new Dictionary(); + + /// iff succeeded + public bool AddIfExists(FirmwareID id, string embedPath) + { + var exists = ReflectionCache.EmbeddedResourceList().Contains(embedPath); + if (exists) EmbedPathMap[id] = embedPath; + return exists; + } + + public string DllPath() + => throw new NotImplementedException(); + + private (string EmbedPath, byte[]? FW) GetFirmwareInner(FirmwareID id) + { + var embedPath = EmbedPathMap[id]; + Stream embeddedResourceStream; + try + { + embeddedResourceStream = ReflectionCache.EmbeddedResourceStream(embedPath); + } + catch (Exception) + { + return (embedPath, null); + } + var fw = embeddedResourceStream.ReadAllBytes(); + embeddedResourceStream.Dispose(); + return (embedPath, fw); + } + + public byte[]? GetFirmware(FirmwareID id, string? msg = null) + { + var (embedPath, fw) = GetFirmwareInner(id); + if (fw is null) Console.WriteLine(FailMsg(embedPath, msg)); + return fw; + } + + public byte[] GetFirmwareOrThrow(FirmwareID id, string? msg = null) + { + var (embedPath, fw) = GetFirmwareInner(id); + if (fw is null) throw new Exception(FailMsg(embedPath, msg)); + return fw; + } + + public (byte[] FW, GameInfo Game) GetFirmwareWithGameInfoOrThrow(FirmwareID id, string? msg = null) + => throw new NotImplementedException(); // only used by PCEHawk + + public string GetRetroSaveRAMDirectory(IGameInfo game) + => throw new NotImplementedException(); + + public string GetRetroSystemPath(IGameInfo game) + => throw new NotImplementedException(); + } + + private sealed class FakeGraphicsControl : IGraphicsControl + { + private readonly IGL_GdiPlus _gdi; + + private readonly Func<(int, int)> _getVirtualSize; + + public Rectangle ClientRectangle + { + get + { + var (w, h) = _getVirtualSize(); + return new(0, 0, w, h); + } + } + + public RenderTargetWrapper? RenderTargetWrapper { get; set; } + + public FakeGraphicsControl(IGL_GdiPlus glImpl, Func<(int Width, int Height)> getVirtualSize) + { + _gdi = glImpl; + _getVirtualSize = getVirtualSize; + } + + public void Begin() + { + _gdi.BeginControl(this); + RenderTargetWrapper!.CreateGraphics(); + } + + public Graphics CreateGraphics() + { + var (w, h) = _getVirtualSize(); + return Graphics.FromImage(new Bitmap(w, h)); + } + + public void End() + => _gdi.EndControl(this); + + public void SetVsync(bool state) {} + + public void SwapBuffers() + { + _gdi.SwapControl(this); + if (RenderTargetWrapper!.MyBufferedGraphics is null) return; + RenderTargetWrapper.CreateGraphics(); + } + + public void Dispose() {} + } + + public sealed class SimpleGDIPDisplayManager : DisplayManagerBase + { + private readonly FakeGraphicsControl _gc; + + private SimpleGDIPDisplayManager(Config config, IEmulator emuCore, IGL_GdiPlus glImpl) + : base(config, emuCore, inputManager: null, movieSession: null, EDispMethod.GdiPlus, glImpl, new GDIPlusGuiRenderer(glImpl)) + { + _gc = (FakeGraphicsControl) glImpl.Internal_CreateGraphicsControl(); + Blank(); + } + + public SimpleGDIPDisplayManager(Config config, IEmulator emuCore, Func<(int Width, int Height)> getVirtualSize) + : this(config, emuCore, new IGL_GdiPlus(self => new FakeGraphicsControl(self, getVirtualSize))) {} + + protected override void ActivateGLContext() + => _gc.Begin(); + + protected override void SwapBuffersOfGraphicsControl() + => _gc.SwapBuffers(); + } + + private static int _totalFrames = 0; + + private static readonly object _totalFramesMutex = new(); + + public static int TotalFrames + { + get + { + lock (_totalFramesMutex) return _totalFrames; + } + } + + /// + /// set-up firmwares on , optionally setting , then + /// initialise and return a core instance ( is provided), + /// and optionally specify a frame number to seek to (e.g. to skip BIOS screens) + /// + public delegate (IEmulator NewCore, int BiosWaitDuration) ClassInitCallbackDelegate( + EmbeddedFirmwareProvider efp, + Config config, + CoreComm coreComm); + + public static Bitmap RunAndScreenshot(ClassInitCallbackDelegate init, Action run) + { + using DummyFrontend fe = new(init); + run(fe); + return fe.Screenshot(); + } + + private readonly Config _config = new(); + + private readonly SimpleController _controller; + + private readonly IVideoProvider _coreAsVP; + + private readonly SimpleGDIPDisplayManager _dispMan; + + public readonly IEmulator Core; + + public readonly IDebuggable? CoreAsDebuggable; + + public readonly IMemoryDomains? CoreAsMemDomains; + + public int FrameCount => Core.Frame; + + /// + public DummyFrontend(ClassInitCallbackDelegate init) + { + EmbeddedFirmwareProvider efp = new(); + var (core, biosWaitDuration) = init( + efp, + _config, + new(Console.WriteLine, Console.WriteLine, efp, CoreComm.CorePreferencesFlags.None)); + Core = core; + _controller = new(Core.ControllerDefinition); + while (Core.Frame < biosWaitDuration) Core.FrameAdvance(_controller, render: false, renderSound: false); + CoreAsDebuggable = Core.CanDebug() ? Core.AsDebuggable() : null; + CoreAsMemDomains = Core.HasMemoryDomains() ? Core.AsMemoryDomains() : null; + _coreAsVP = core.AsVideoProvider(); + _dispMan = new(_config, core, () => (_coreAsVP!.VirtualWidth, _coreAsVP.VirtualHeight)); + } + + public void Dispose() + { + _dispMan.Dispose(); + lock (_totalFramesMutex) _totalFrames += FrameCount; + } + + public void FrameAdvance() + => Core.FrameAdvance(_controller, render: false, renderSound: false); + + public void FrameAdvanceBy(int numFrames) + => FrameAdvanceTo(FrameCount + numFrames); + + /// last return of (will be iff timed out) + /// is NOT relative to current frame count + public bool FrameAdvanceUntil(Func pred, int timeoutAtFrame = 500) + { + while (!pred() && FrameCount < timeoutAtFrame) FrameAdvance(); + return FrameCount < timeoutAtFrame; + } + + public void FrameAdvanceTo(int frame) + { + while (FrameCount < frame) FrameAdvance(); + } + + public Bitmap Screenshot() + => _dispMan.RenderVideoProvider(_coreAsVP).ToSysdrawingBitmap(); + + public void SetButton(string buttonName) + => _controller[buttonName] = true; + } +} diff --git a/src/BizHawk.Tests.Testroms.GB/GB_GBC/AcidTestroms.cs b/src/BizHawk.Tests.Testroms.GB/GB_GBC/AcidTestroms.cs new file mode 100644 index 0000000000..f2ec33e1fe --- /dev/null +++ b/src/BizHawk.Tests.Testroms.GB/GB_GBC/AcidTestroms.cs @@ -0,0 +1,126 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +using BizHawk.Common.IOExtensions; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using static BizHawk.Tests.Testroms.GB.GBHelper; + +namespace BizHawk.Tests.Testroms.GB +{ + [TestClass] + public sealed class AcidTestroms + { + public readonly struct AcidTestCase + { + public string ExpectEmbedPath => TestName switch + { + "cgb-acid-hell" => "res.cgb_acid_hell_artifact.reference.png", + "cgb-acid2" => "res.cgb_acid2_artifact.reference.png", + "dmg-acid2" => $"res.dmg_acid2_artifact.reference-{(Setup.Variant.IsColour() ? "cgb" : "dmg")}.png", + _ => throw new Exception() + }; + + public readonly string RomEmbedPath => TestName switch + { + "cgb-acid-hell" => "res.cgb_acid_hell_artifact.cgb-acid-hell.gbc", + "cgb-acid2" => "res.cgb_acid2_artifact.cgb-acid2.gbc", + "dmg-acid2" => "res.dmg_acid2_artifact.dmg-acid2.gb", + _ => throw new Exception() + }; + + public readonly CoreSetup Setup; + + public readonly string TestName; + + public AcidTestCase(string testName, CoreSetup setup) + { + Setup = setup; + TestName = testName; + } + + public readonly string DisplayName() + => $"{TestName} on {Setup}"; + } + + [AttributeUsage(AttributeTargets.Method)] + private sealed class AcidTestDataAttribute : Attribute, ITestDataSource + { + public IEnumerable GetData(MethodInfo methodInfo) + { + List testCases = new(); + foreach (var setup in CoreSetup.ValidSetupsFor(ConsoleVariant.CGB_C)) + { + testCases.Add(new("cgb-acid-hell", setup)); + testCases.Add(new("cgb-acid2", setup)); + testCases.Add(new("dmg-acid2", setup)); + } + foreach (var setup in CoreSetup.ValidSetupsFor(ConsoleVariant.DMG)) + { + testCases.Add(new("dmg-acid2", setup)); + } +// testCases.RemoveAll(static testCase => testCase.Setup.Variant is not ConsoleVariant.DMG); // uncomment and modify to run a subset of the test cases... + testCases.RemoveAll(static testCase => TestUtils.ShouldIgnoreCase(SUITE_ID, testCase.DisplayName())); // ...or use the global blocklist in TestUtils + return testCases.OrderBy(static testCase => testCase.DisplayName()) + .Select(static testCase => new object?[] { testCase }); + } + + public string GetDisplayName(MethodInfo methodInfo, object?[] data) + => $"{methodInfo.Name}({((AcidTestCase) data[0]!).DisplayName()})"; + } + + private const string SUITE_ID = "AcidTestroms"; + + private static readonly IReadOnlyList FilteredEmbedPaths = ReflectionCache.EmbeddedResourceList().Where(static s => s.Contains("acid")).ToList(); + + private static readonly IReadOnlyCollection KnownFailures = new[] + { + "cgb-acid-hell on CGB_C in Gambatte", + "cgb-acid-hell on CGB_C in Gambatte (no BIOS)", + "dmg-acid2 on CGB_C in Gambatte", + "dmg-acid2 on CGB_C in Gambatte (no BIOS)", + }; + + [ClassCleanup] + public static void AfterAll() + => TestUtils.WriteMetricsToDisk(); + + [ClassInitialize] + public static void BeforeAll(TestContext ctx) + => TestUtils.PrepareDBAndOutput(SUITE_ID); + + [AcidTestData] + [DataTestMethod] + public void RunAcidTest(AcidTestCase testCase) + { + TestUtils.ShortCircuitMissingRom(isPresent: FilteredEmbedPaths.Contains(testCase.RomEmbedPath)); + var caseStr = testCase.DisplayName(); + TestUtils.ShortCircuitKnownFailure(caseStr, KnownFailures, out var knownFail); + var actualUnnormalised = DummyFrontend.RunAndScreenshot( + InitGBCore(testCase.Setup, $"{testCase.TestName}.gbc", ReflectionCache.EmbeddedResourceStream(testCase.RomEmbedPath).ReadAllBytes()), + static fe => fe.FrameAdvanceBy(15)); + var state = GBScreenshotsEqual( + ReflectionCache.EmbeddedResourceStream(testCase.ExpectEmbedPath), + actualUnnormalised, + knownFail, + testCase.Setup, + (SUITE_ID, caseStr), + MattCurriePaletteMap); + switch (state) + { + case TestUtils.TestSuccessState.ExpectedFailure: + Assert.Inconclusive("expected failure, verified"); + break; + case TestUtils.TestSuccessState.Failure: + Assert.Fail("expected and actual screenshots differ"); + break; + case TestUtils.TestSuccessState.UnexpectedSuccess: + Assert.Fail("expected and actual screenshots matched unexpectedly (this is a good thing)"); + break; + } + } + } +} diff --git a/src/BizHawk.Tests.Testroms.GB/GB_GBC/BullyGB.cs b/src/BizHawk.Tests.Testroms.GB/GB_GBC/BullyGB.cs new file mode 100644 index 0000000000..7ca4513ca8 --- /dev/null +++ b/src/BizHawk.Tests.Testroms.GB/GB_GBC/BullyGB.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +using BizHawk.Common.IOExtensions; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using static BizHawk.Tests.Testroms.GB.GBHelper; + +namespace BizHawk.Tests.Testroms.GB +{ + [TestClass] + public sealed class BullyGB + { + [AttributeUsage(AttributeTargets.Method)] + private sealed class BullyTestData : Attribute, ITestDataSource + { + public IEnumerable GetData(MethodInfo methodInfo) + { + var testCases = new[] { ConsoleVariant.CGB_C, ConsoleVariant.DMG }.SelectMany(CoreSetup.ValidSetupsFor).ToList(); +// testCases.RemoveAll(static setup => setup.Variant is not ConsoleVariant.DMG); // uncomment and modify to run a subset of the test cases... + testCases.RemoveAll(static setup => TestUtils.ShouldIgnoreCase(SUITE_ID, DisplayNameFor(setup))); // ...or use the global blocklist in TestUtils + return testCases.OrderBy(static setup => setup.ToString()) + .Select(static setup => new object?[] { setup }); + } + + public string GetDisplayName(MethodInfo methodInfo, object?[] data) + => $"{methodInfo.Name}({(CoreSetup) data[0]!})"; + } + + private const string ROM_EMBED_PATH = "res.BullyGB_artifact.bully.gb"; + + private const string SUITE_ID = "BullyGB"; + + private static readonly IReadOnlyCollection KnownFailures = new[] + { + "BullyGB on CGB_C in GBHawk", + }; + + private static readonly bool RomIsPresent = ReflectionCache.EmbeddedResourceList().Contains(ROM_EMBED_PATH); + + [ClassCleanup] + public static void AfterAll() + => TestUtils.WriteMetricsToDisk(); + + [ClassInitialize] + public static void BeforeAll(TestContext ctx) + => TestUtils.PrepareDBAndOutput(SUITE_ID); + + private static string DisplayNameFor(CoreSetup setup) + => $"BullyGB on {setup}"; + + [BullyTestData] + [DataTestMethod] + public void RunBullyTest(CoreSetup setup) + { + TestUtils.ShortCircuitMissingRom(RomIsPresent); + var caseStr = DisplayNameFor(setup); + TestUtils.ShortCircuitKnownFailure(caseStr, KnownFailures, out var knownFail); + var actualUnnormalised = DummyFrontend.RunAndScreenshot( + InitGBCore(setup, "bully.gbc", ReflectionCache.EmbeddedResourceStream(ROM_EMBED_PATH).ReadAllBytes()), + static fe => fe.FrameAdvanceBy(18)); + var state = GBScreenshotsEqual( + ReflectionCache.EmbeddedResourceStream($"res.BullyGB_artifact.expected_{(setup.Variant.IsColour() ? "cgb" : "dmg")}.png"), + actualUnnormalised, + knownFail, + setup, + (SUITE_ID, caseStr)); + switch (state) + { + case TestUtils.TestSuccessState.ExpectedFailure: + Assert.Inconclusive("expected failure, verified"); + break; + case TestUtils.TestSuccessState.Failure: + Assert.Fail("expected and actual screenshots differ"); + break; + case TestUtils.TestSuccessState.UnexpectedSuccess: + Assert.Fail("expected and actual screenshots matched unexpectedly (this is a good thing)"); + break; + } + } + } +} diff --git a/src/BizHawk.Tests.Testroms.GB/GB_GBC/GBHelper.cs b/src/BizHawk.Tests.Testroms.GB/GB_GBC/GBHelper.cs new file mode 100644 index 0000000000..b598469ee4 --- /dev/null +++ b/src/BizHawk.Tests.Testroms.GB/GB_GBC/GBHelper.cs @@ -0,0 +1,161 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; + +using BizHawk.Emulation.Common; +using BizHawk.Emulation.Cores; +using BizHawk.Emulation.Cores.Nintendo.Gameboy; +using BizHawk.Emulation.Cores.Nintendo.GBHawk; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using static BizHawk.Emulation.Cores.Nintendo.Gameboy.Gameboy; +using static BizHawk.Emulation.Cores.Nintendo.GBHawk.GBHawk; + +namespace BizHawk.Tests.Testroms.GB +{ + public static class GBHelper + { + public enum ConsoleVariant { CGB_C, CGB_D, DMG, DMG_B } + + public readonly struct CoreSetup + { + public static IReadOnlyCollection ValidSetupsFor(ConsoleVariant variant) + => new CoreSetup[] { new(CoreNames.Gambatte, variant), new(CoreNames.Gambatte, variant, useBios: false), new(CoreNames.GbHawk, variant) }; + + public readonly string CoreName; + + public readonly bool UseBIOS; + + public readonly ConsoleVariant Variant; + + public CoreSetup(string coreName, ConsoleVariant variant, bool useBios = true) + { + CoreName = coreName; + UseBIOS = useBios; + Variant = variant; + } + + public override readonly string ToString() + => $"{Variant} in {CoreName}{(UseBIOS ? string.Empty : " (no BIOS)")}"; + } + + private static readonly GambatteSettings GambatteSettings = new() { CGBColors = GBColors.ColorType.vivid }; + + private static readonly GambatteSyncSettings GambatteSyncSettings_GB_NOBIOS = new() { ConsoleMode = GambatteSyncSettings.ConsoleModeType.GB, FrameLength = GambatteSyncSettings.FrameLengthType.EqualLengthFrames }; + + private static readonly GambatteSyncSettings GambatteSyncSettings_GB_USEBIOS = new() { ConsoleMode = GambatteSyncSettings.ConsoleModeType.GB, EnableBIOS = true, FrameLength = GambatteSyncSettings.FrameLengthType.EqualLengthFrames }; + + private static readonly GambatteSyncSettings GambatteSyncSettings_GBC_NOBIOS = new() { ConsoleMode = GambatteSyncSettings.ConsoleModeType.GBC, FrameLength = GambatteSyncSettings.FrameLengthType.EqualLengthFrames }; + + private static readonly GambatteSyncSettings GambatteSyncSettings_GBC_USEBIOS = new() { ConsoleMode = GambatteSyncSettings.ConsoleModeType.GBC, EnableBIOS = true, FrameLength = GambatteSyncSettings.FrameLengthType.EqualLengthFrames }; + + private static readonly GBSyncSettings GBHawkSyncSettings_GB = new() { ConsoleMode = GBSyncSettings.ConsoleModeType.GB }; + + private static readonly GBSyncSettings GBHawkSyncSettings_GBC = new() { ConsoleMode = GBSyncSettings.ConsoleModeType.GBC }; + + public static readonly IReadOnlyDictionary MattCurriePaletteMap = new Dictionary + { + [0x0F3EAA] = 0x0000FF, + [0x137213] = 0x009C00, + [0x187890] = 0x0063C6, + [0x695423] = 0x737300, + [0x7BC8D5] = 0x6BBDFF, + [0x7F3848] = 0x943939, + [0x83C656] = 0x7BFF31, + [0x9D7E34] = 0xADAD00, + [0xE18096] = 0xFF8484, + [0xE8BA4D] = 0xFFFF00, + [0xF8F8F8] = 0xFFFFFF, + }; + + public static readonly IReadOnlyDictionary UnVividGBCPaletteMap = new Dictionary + { + [0x0063C5] = 0x0063C6, + [0x00CE00] = 0x199619, + [0x089C84] = 0x21926C, + [0x424242] = 0x404040, + [0x52AD52] = 0x5B925B, + [0x943A3A] = 0x943939, + [0xA5A5A5] = 0xA0A0A0, + [0xAD52AD] = 0x9D669D, + [0xFFFFFF] = 0xF8F8F8, + }; + + public static readonly IReadOnlyDictionary UnVividGBPaletteMap = new Dictionary + { + [0x525252] = 0x555555, + [0xADADAD] = 0xAAAAAA, + }; + + private static bool AddEmbeddedGBBIOS(this DummyFrontend.EmbeddedFirmwareProvider efp, ConsoleVariant variant) + => variant.IsColour() + ? efp.AddIfExists(new("GBC", "World"), false ? "res.fw.GBC__World__AGB.bin" : "res.fw.GBC__World__CGB.bin") + : efp.AddIfExists(new("GB", "World"), "res.fw.GB__World__DMG.bin"); + + public static TestUtils.TestSuccessState GBScreenshotsEqual( + Stream expectFile, + Image? actualUnnormalised, + bool expectingNotEqual, + CoreSetup setup, + (string Suite, string Case) id, + IReadOnlyDictionary? extraPaletteMap = null) + { + if (actualUnnormalised is null) + { + Assert.Fail("actual screenshot was null"); + return TestUtils.TestSuccessState.Failure; // never hit + } + var actual = NormaliseGBScreenshot(actualUnnormalised, setup); +// ImageUtils.PrintPalette(Image.FromStream(expectFile), "expected image", actual, "actual image (after normalisation, before extra map)"); + return ImageUtils.ScreenshotsEqualMagickDotNET( + expectFile, + extraPaletteMap is null ? actual : ImageUtils.PaletteSwap(actual, extraPaletteMap), + expectingNotEqual, + id); + } + + public static GambatteSyncSettings GetGambatteSyncSettings(ConsoleVariant variant, bool biosAvailable) + => biosAvailable + ? variant.IsColour() + ? GambatteSyncSettings_GBC_USEBIOS + : GambatteSyncSettings_GB_USEBIOS + : variant.IsColour() + ? GambatteSyncSettings_GBC_NOBIOS + : GambatteSyncSettings_GB_NOBIOS; + + public static GBSyncSettings GetGBHawkSyncSettings(ConsoleVariant variant) + => variant.IsColour() + ? GBHawkSyncSettings_GBC + : GBHawkSyncSettings_GB; + + public static DummyFrontend.ClassInitCallbackDelegate InitGBCore(CoreSetup setup, string romFilename, byte[] rom) + => (efp, _, coreComm) => + { + if (setup.UseBIOS && !efp.AddEmbeddedGBBIOS(setup.Variant)) Assert.Inconclusive("BIOS not provided"); + var game = Database.GetGameInfo(rom, romFilename); + IEmulator newCore = setup.CoreName switch + { + CoreNames.Gambatte => new Gameboy(coreComm, game, rom, GambatteSettings, GetGambatteSyncSettings(setup.Variant, setup.UseBIOS), deterministic: true), + CoreNames.GbHawk => new GBHawk(coreComm, game, rom, new(), GetGBHawkSyncSettings(setup.Variant)), + _ => throw new Exception() + }; + var biosWaitDuration = setup.UseBIOS + ? setup.Variant.IsColour() + ? 186 + : 334 + : 0; + return (newCore, biosWaitDuration); + }; + + public static bool IsColour(this ConsoleVariant variant) + => variant is ConsoleVariant.CGB_C or ConsoleVariant.CGB_D; + + /// converts Gambatte's GBC palette to GBHawk's; GB palette is the same + public static Image NormaliseGBScreenshot(Image img, CoreSetup setup) + => setup.CoreName is CoreNames.Gambatte + ? ImageUtils.PaletteSwap(img, setup.Variant.IsColour() ? UnVividGBCPaletteMap : UnVividGBPaletteMap) + : img; + } +} diff --git a/src/BizHawk.Tests.Testroms.GB/GB_GBC/MealybugTearoomTests.cs b/src/BizHawk.Tests.Testroms.GB/GB_GBC/MealybugTearoomTests.cs new file mode 100644 index 0000000000..56eca59731 --- /dev/null +++ b/src/BizHawk.Tests.Testroms.GB/GB_GBC/MealybugTearoomTests.cs @@ -0,0 +1,265 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +using BizHawk.Common.IOExtensions; +using BizHawk.Common.StringExtensions; +using BizHawk.Emulation.Common; +using BizHawk.Emulation.Cores; +using BizHawk.Emulation.Cores.Nintendo.Gameboy; +using BizHawk.Emulation.Cores.Nintendo.GBHawk; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using static BizHawk.Tests.Testroms.GB.GBHelper; + +namespace BizHawk.Tests.Testroms.GB +{ + [TestClass] + public sealed class MealybugTearoomTests + { + public readonly struct MealybugTestCase + { + public static readonly MealybugTestCase Dummy = new("missing_files", new(CoreNames.Gambatte, ConsoleVariant.DMG), string.Empty, string.Empty); + + public static readonly IReadOnlyCollection KnownFailures = new[] + { + "m3_bgp_change on CGB_C in Gambatte", // Gambatte's GBC emulation matches CGB D variant + "m3_bgp_change on CGB_C in Gambatte (no BIOS)", // Gambatte's GBC emulation matches CGB D variant + "m3_bgp_change on CGB_C in GBHawk", + "m3_bgp_change on CGB_D in GBHawk", + "m3_bgp_change on DMG in Gambatte", + "m3_bgp_change on DMG in Gambatte (no BIOS)", + "m3_bgp_change on DMG in GBHawk", + "m3_bgp_change_sprites on CGB_C in Gambatte", // Gambatte's GBC emulation matches CGB D variant + "m3_bgp_change_sprites on CGB_C in Gambatte (no BIOS)", // Gambatte's GBC emulation matches CGB D variant + "m3_bgp_change_sprites on CGB_C in GBHawk", + "m3_bgp_change_sprites on CGB_D in GBHawk", + "m3_bgp_change_sprites on DMG in Gambatte", + "m3_bgp_change_sprites on DMG in Gambatte (no BIOS)", + "m3_bgp_change_sprites on DMG in GBHawk", + "m3_lcdc_bg_en_change on DMG in Gambatte", + "m3_lcdc_bg_en_change on DMG in Gambatte (no BIOS)", + "m3_lcdc_bg_en_change on DMG in GBHawk", + "m3_lcdc_bg_en_change on DMG_B in Gambatte", + "m3_lcdc_bg_en_change on DMG_B in Gambatte (no BIOS)", + "m3_lcdc_bg_en_change on DMG_B in GBHawk", + "m3_lcdc_bg_map_change on CGB_C in GBHawk", + "m3_lcdc_bg_map_change on DMG in GBHawk", + "m3_lcdc_bg_map_change2 on CGB_C in GBHawk", + "m3_lcdc_obj_en_change on DMG in Gambatte", + "m3_lcdc_obj_en_change on DMG in Gambatte (no BIOS)", + "m3_lcdc_obj_en_change on DMG in GBHawk", + "m3_lcdc_obj_en_change_variant on CGB_C in Gambatte", // Gambatte's GBC emulation matches CGB D variant + "m3_lcdc_obj_en_change_variant on CGB_C in Gambatte (no BIOS)", // Gambatte's GBC emulation matches CGB D variant + "m3_lcdc_obj_en_change_variant on CGB_C in GBHawk", + "m3_lcdc_obj_en_change_variant on CGB_D in GBHawk", + "m3_lcdc_obj_en_change_variant on DMG in Gambatte", + "m3_lcdc_obj_en_change_variant on DMG in Gambatte (no BIOS)", + "m3_lcdc_obj_en_change_variant on DMG in GBHawk", + "m3_lcdc_obj_size_change on CGB_C in GBHawk", + "m3_lcdc_obj_size_change on DMG in Gambatte", + "m3_lcdc_obj_size_change on DMG in Gambatte (no BIOS)", + "m3_lcdc_obj_size_change on DMG in GBHawk", + "m3_lcdc_obj_size_change_scx on CGB_C in GBHawk", + "m3_lcdc_obj_size_change_scx on DMG in Gambatte", + "m3_lcdc_obj_size_change_scx on DMG in Gambatte (no BIOS)", + "m3_lcdc_obj_size_change_scx on DMG in GBHawk", + "m3_lcdc_tile_sel_change on CGB_C in Gambatte", + "m3_lcdc_tile_sel_change on CGB_C in Gambatte (no BIOS)", + "m3_lcdc_tile_sel_change on CGB_C in GBHawk", + "m3_lcdc_tile_sel_change on DMG in GBHawk", + "m3_lcdc_tile_sel_change2 on CGB_C in Gambatte", + "m3_lcdc_tile_sel_change2 on CGB_C in Gambatte (no BIOS)", + "m3_lcdc_tile_sel_change2 on CGB_C in GBHawk", + "m3_lcdc_tile_sel_win_change on CGB_C in Gambatte", + "m3_lcdc_tile_sel_win_change on CGB_C in Gambatte (no BIOS)", + "m3_lcdc_tile_sel_win_change on CGB_C in GBHawk", + "m3_lcdc_tile_sel_win_change on DMG in GBHawk", + "m3_lcdc_tile_sel_win_change2 on CGB_C in Gambatte", + "m3_lcdc_tile_sel_win_change2 on CGB_C in Gambatte (no BIOS)", + "m3_lcdc_tile_sel_win_change2 on CGB_C in GBHawk", + "m3_lcdc_win_en_change_multiple on CGB_C in GBHawk", + "m3_lcdc_win_en_change_multiple on DMG in GBHawk", + "m3_lcdc_win_en_change_multiple_wx on DMG in Gambatte", + "m3_lcdc_win_en_change_multiple_wx on DMG in Gambatte (no BIOS)", + "m3_lcdc_win_en_change_multiple_wx on DMG in GBHawk", + "m3_lcdc_win_en_change_multiple_wx on DMG_B in Gambatte", + "m3_lcdc_win_en_change_multiple_wx on DMG_B in Gambatte (no BIOS)", + "m3_lcdc_win_en_change_multiple_wx on DMG_B in GBHawk", + "m3_lcdc_win_map_change on CGB_C in GBHawk", + "m3_lcdc_win_map_change on DMG in GBHawk", + "m3_lcdc_win_map_change2 on CGB_C in GBHawk", + "m3_obp0_change on CGB_C in Gambatte", // Gambatte's GBC emulation matches CGB D variant + "m3_obp0_change on CGB_C in Gambatte (no BIOS)", // Gambatte's GBC emulation matches CGB D variant + "m3_obp0_change on CGB_C in GBHawk", + "m3_obp0_change on CGB_D in GBHawk", + "m3_obp0_change on DMG in GBHawk", + "m3_scx_high_5_bits on CGB_C in GBHawk", + "m3_scx_high_5_bits on DMG in GBHawk", + "m3_scx_high_5_bits_change2 on CGB_C in GBHawk", + "m3_scy_change on CGB_C in GBHawk", + "m3_scy_change on CGB_D in Gambatte", // Gambatte's GBC emulation matches CGB C variant + "m3_scy_change on CGB_D in Gambatte (no BIOS)", // Gambatte's GBC emulation matches CGB C variant + "m3_scy_change on CGB_D in GBHawk", + "m3_scy_change on DMG in GBHawk", + "m3_scy_change2 on CGB_C in GBHawk", + "m3_window_timing on CGB_C in Gambatte", // Gambatte's GBC emulation matches CGB D variant + "m3_window_timing on CGB_C in Gambatte (no BIOS)", // Gambatte's GBC emulation matches CGB D variant + "m3_window_timing on CGB_C in GBHawk", + "m3_window_timing on CGB_D in GBHawk", + "m3_window_timing on DMG in GBHawk", + "m3_window_timing_wx_0 on CGB_C in Gambatte", + "m3_window_timing_wx_0 on CGB_C in Gambatte (no BIOS)", + "m3_window_timing_wx_0 on CGB_C in GBHawk", + "m3_window_timing_wx_0 on CGB_D in Gambatte", + "m3_window_timing_wx_0 on CGB_D in Gambatte (no BIOS)", + "m3_window_timing_wx_0 on CGB_D in GBHawk", + "m3_window_timing_wx_0 on DMG in Gambatte", + "m3_window_timing_wx_0 on DMG in Gambatte (no BIOS)", + "m3_window_timing_wx_0 on DMG in GBHawk", + "m3_wx_4_change on DMG in Gambatte", + "m3_wx_4_change on DMG in Gambatte (no BIOS)", + "m3_wx_4_change on DMG in GBHawk", + "m3_wx_4_change_sprites on CGB_C in Gambatte", + "m3_wx_4_change_sprites on CGB_C in Gambatte (no BIOS)", + "m3_wx_4_change_sprites on CGB_C in GBHawk", + "m3_wx_4_change_sprites on DMG in Gambatte", + "m3_wx_4_change_sprites on DMG in Gambatte (no BIOS)", + "m3_wx_4_change_sprites on DMG in GBHawk", + "m3_wx_5_change on DMG in Gambatte", + "m3_wx_5_change on DMG in Gambatte (no BIOS)", + "m3_wx_5_change on DMG in GBHawk", + "m3_wx_6_change on DMG in GBHawk", + }; + + public readonly string ExpectEmbedPath; + + public readonly string RomEmbedPath; + + public readonly CoreSetup Setup; + + public readonly string TestName; + + public MealybugTestCase(string testName, CoreSetup setup, string romEmbedPath, string expectEmbedPath) + { + TestName = testName; + Setup = setup; + RomEmbedPath = romEmbedPath; + ExpectEmbedPath = expectEmbedPath; + } + + public readonly string DisplayName() + => $"{TestName} on {Setup}"; + } + + [AttributeUsage(AttributeTargets.Method)] + private sealed class MealybugTestDataAttribute : Attribute, ITestDataSource + { + public IEnumerable GetData(MethodInfo methodInfo) + { + if (!RomsArePresent) return new[] { new object?[] { MealybugTestCase.Dummy } }; + var variants = new[] { ("expected.CPU_CGB_C.", ConsoleVariant.CGB_C), ("expected.CPU_CGB_D.", ConsoleVariant.CGB_D), ("expected.DMG_blob.", ConsoleVariant.DMG), ("expected.DMG_CPU_B.", ConsoleVariant.DMG_B) }; + List testCases = new(); + foreach (var item in ReflectionCache.EmbeddedResourceList(SUITE_PREFIX).Where(static item => item.EndsWith(".png"))) + { + var (prefix, variant) = variants.First(kvp => item.StartsWith(kvp.Item1)); + var testName = item.RemovePrefix(prefix).RemoveSuffix(".png"); + var romEmbedPath = SUITE_PREFIX + $"build.ppu.{testName}.gb"; + var expectEmbedPath = SUITE_PREFIX + item; + foreach (var setup in CoreSetup.ValidSetupsFor(variant)) testCases.Add(new(testName, setup, romEmbedPath, expectEmbedPath)); + } + // expected value is a "no screenshot available" message + testCases.RemoveAll(static testCase => + testCase.Setup.Variant is ConsoleVariant.CGB_C or ConsoleVariant.CGB_D + && testCase.TestName is "m3_lcdc_win_en_change_multiple_wx" or "m3_wx_4_change" or "m3_wx_5_change" or "m3_wx_6_change"); + // these are identical to CGB_C + testCases.RemoveAll(static testCase => + testCase.Setup.Variant is ConsoleVariant.CGB_D + && testCase.TestName is "m2_win_en_toggle" or "m3_lcdc_bg_en_change" or "m3_lcdc_bg_map_change" or "m3_lcdc_obj_en_change" or "m3_lcdc_obj_size_change" or "m3_lcdc_obj_size_change_scx" or "m3_lcdc_tile_sel_change" or "m3_lcdc_tile_sel_win_change" or "m3_lcdc_win_en_change_multiple" or "m3_lcdc_win_map_change" or "m3_scx_high_5_bits" or "m3_scx_low_3_bits" or "m3_wx_4_change" or "m3_wx_4_change_sprites" or "m3_wx_5_change" or "m3_wx_6_change"); + +// testCases.RemoveAll(static testCase => testCase.Setup.Variant is not ConsoleVariant.DMG); // uncomment and modify to run a subset of the test cases... + testCases.RemoveAll(static testCase => TestUtils.ShouldIgnoreCase(SUITE_ID, testCase.DisplayName())); // ...or use the global blocklist in TestUtils + return testCases.OrderBy(static testCase => testCase.DisplayName()) + .Select(static testCase => new object?[] { testCase }); + } + + public string GetDisplayName(MethodInfo methodInfo, object?[] data) + => $"{methodInfo.Name}({((MealybugTestCase) data[0]!).DisplayName()})"; + } + + private const string SUITE_ID = "Mealybug"; + + private const string SUITE_PREFIX = "res.mealybug_tearoom_tests_artifact."; + + private static readonly bool RomsArePresent = ReflectionCache.EmbeddedResourceList().Any(static s => s.StartsWith(SUITE_PREFIX)); + + [ClassCleanup] + public static void AfterAll() + => TestUtils.WriteMetricsToDisk(); + + [ClassInitialize] + public static void BeforeAll(TestContext ctx) + => TestUtils.PrepareDBAndOutput(SUITE_ID); + + [DataTestMethod] + [MealybugTestData] + public void RunMealybugTest(MealybugTestCase testCase) + { + TestUtils.ShortCircuitMissingRom(RomsArePresent); + var caseStr = testCase.DisplayName(); + TestUtils.ShortCircuitKnownFailure(caseStr, MealybugTestCase.KnownFailures, out var knownFail); + void ExecTest(DummyFrontend fe) + { + if (testCase.Setup.CoreName is CoreNames.Gambatte) + { + // without this, exec hook triggers too early and I've decided I don't want to know why ¯\_(ツ)_/¯ --yoshi + fe.FrameAdvanceBy(5); + if (testCase.TestName is "m3_lcdc_win_map_change2") fe.FrameAdvance(); // just happens to be an outlier + } + var domain = fe.CoreAsMemDomains!.SystemBus; + Func derefPC = fe.Core switch + { + Gameboy => () => domain.PeekByte((long) fe.CoreAsDebuggable!.GetCpuFlagsAndRegisters()["PC"].Value), + GBHawk gbHawk => () => domain.PeekByte(gbHawk.cpu.RegPC), + _ => throw new Exception() + }; + var finished = false; + fe.CoreAsDebuggable!.MemoryCallbacks.Add(new MemoryCallback( + domain.Name, + MemoryCallbackType.Execute, + "breakpoint", + (_, _, _) => + { + if (!finished && derefPC() is 0x40) finished = true; + }, + address: null, // all addresses + mask: null)); + Assert.IsTrue(fe.FrameAdvanceUntil(() => finished), "timed out waiting for exec hook"); + } + var actualUnnormalised = DummyFrontend.RunAndScreenshot( + InitGBCore(testCase.Setup, $"{testCase.TestName}.gb", ReflectionCache.EmbeddedResourceStream(testCase.RomEmbedPath).ReadAllBytes()), + ExecTest); + var state = GBScreenshotsEqual( + ReflectionCache.EmbeddedResourceStream(testCase.ExpectEmbedPath), + actualUnnormalised, + knownFail, + testCase.Setup, + (SUITE_ID, caseStr), + MattCurriePaletteMap); + switch (state) + { + case TestUtils.TestSuccessState.ExpectedFailure: + Assert.Inconclusive("expected failure, verified"); + break; + case TestUtils.TestSuccessState.Failure: + Assert.Fail("expected and actual screenshots differ"); + break; + case TestUtils.TestSuccessState.UnexpectedSuccess: + Assert.Fail("expected and actual screenshots matched unexpectedly (this is a good thing)"); + break; + } + } + } +} diff --git a/src/BizHawk.Tests.Testroms.GB/GB_GBC/RTC3Test.cs b/src/BizHawk.Tests.Testroms.GB/GB_GBC/RTC3Test.cs new file mode 100644 index 0000000000..9f530a9487 --- /dev/null +++ b/src/BizHawk.Tests.Testroms.GB/GB_GBC/RTC3Test.cs @@ -0,0 +1,119 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Reflection; + +using BizHawk.Common.IOExtensions; +using BizHawk.Emulation.Cores; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using static BizHawk.Tests.Testroms.GB.GBHelper; + +namespace BizHawk.Tests.Testroms.GB +{ + [TestClass] + public sealed class RTC3Test + { + [AttributeUsage(AttributeTargets.Method)] + private sealed class RTC3TestData : Attribute, ITestDataSource + { + public IEnumerable GetData(MethodInfo methodInfo) + { + var testCases = new[] { ConsoleVariant.CGB_C, ConsoleVariant.DMG }.SelectMany(CoreSetup.ValidSetupsFor).ToList(); +// testCases.RemoveAll(static setup => setup.Variant is not ConsoleVariant.DMG); // uncomment and modify to run a subset of the test cases... + foreach (var subTest in new[] { "basic", "range", "subSecond" }) + { + testCases.RemoveAll(setup => TestUtils.ShouldIgnoreCase(SUITE_ID, DisplayNameFor(setup, subTest))); // ...or use the global blocklist in TestUtils + } + return testCases.OrderBy(static setup => setup.ToString()) + .Select(static setup => new object?[] { setup }); + } + + public string GetDisplayName(MethodInfo methodInfo, object?[] data) + => $"{methodInfo.Name}({(CoreSetup) data[0]!})"; + } + + private const string ROM_EMBED_PATH = "res.rtc3test_artifact.rtc3test.gb"; + + private const string SUITE_ID = "RTC3Test"; + + private static readonly IReadOnlyCollection KnownFailures = new[] + { + "", + }; + + private static readonly bool RomIsPresent = ReflectionCache.EmbeddedResourceList().Contains(ROM_EMBED_PATH); + + [ClassCleanup] + public static void AfterAll() + => TestUtils.WriteMetricsToDisk(); + + [ClassInitialize] + public static void BeforeAll(TestContext ctx) + => TestUtils.PrepareDBAndOutput(SUITE_ID); + + private static string DisplayNameFor(CoreSetup setup, string subTest) + => $"RTC3Test.{subTest} on {setup}"; + + [DataTestMethod] + [RTC3TestData] + public void RunRTC3Test(CoreSetup setup) + { + TestUtils.ShortCircuitMissingRom(RomIsPresent); + TestUtils.ShortCircuitKnownFailure(new[] { "basic", "range", "subSecond" }.Select(subTest => DisplayNameFor(setup, subTest)).All(KnownFailures.Contains)); + DummyFrontend fe = new(InitGBCore(setup, "rtc3test.gb", ReflectionCache.EmbeddedResourceStream(ROM_EMBED_PATH).ReadAllBytes())); + bool DoSubcaseAssertion(string subTest, Bitmap actualUnnormalised) + { + var caseStr = DisplayNameFor(setup, subTest); + var knownFail = TestUtils.IsKnownFailure(caseStr, KnownFailures); + var state = GBScreenshotsEqual( + ReflectionCache.EmbeddedResourceStream($"res.rtc3test_artifact.expected_{subTest.ToLowerInvariant()}_{(setup.Variant.IsColour() ? "cgb" : "dmg")}.png"), + actualUnnormalised, + knownFail, + setup, + (SUITE_ID, caseStr)); + switch (state) + { + case TestUtils.TestSuccessState.ExpectedFailure: + Console.WriteLine("expected failure, verified"); + break; + case TestUtils.TestSuccessState.Failure: + Assert.Fail("expected and actual screenshots differ"); + break; + case TestUtils.TestSuccessState.Success: + return true; + case TestUtils.TestSuccessState.UnexpectedSuccess: + Assert.Fail("expected and actual screenshots matched unexpectedly (this is a good thing)"); + break; + } + return false; + } + var (buttonA, buttonDown) = setup.CoreName is CoreNames.Gambatte ? ("A", "Down") : ("P1 A", "P1 Down"); + fe.FrameAdvanceBy(6); + fe.SetButton(buttonA); +// fe.FrameAdvanceBy(setup.Variant.IsColour() ? 676 : 648); + fe.FrameAdvanceBy(685); + var basicPassed = DoSubcaseAssertion("basic", fe.Screenshot()); +#if true + fe.Dispose(); + if (!basicPassed) Assert.Inconclusive(); // for this to be false, it must have been an expected failure or execution would have stopped with an Assert.Fail call + Assert.Inconclusive("(other subtests aren't implemented)"); +#else // screenshot seems to freeze emulation, or at least rendering + fe.SetButton(buttonA); + fe.FrameAdvanceBy(3); + fe.SetButton(buttonDown); + fe.FrameAdvanceBy(2); + fe.SetButton(buttonA); + fe.FrameAdvanceBy(429); + var rangePassed = DoSubcaseAssertion("range", fe.Screenshot()); + fe.SetButton(buttonA); + // didn't bother TASing the remaining menu navigation because it doesn't work +// var subSecondPassed = DoSubcaseAssertion("subSecond", fe.Screenshot()); + fe.Dispose(); + if (!(basicPassed && rangePassed /*&& subSecondPassed*/)) Assert.Inconclusive(); // for one of these to be false, it must have been an expected failure or execution would have stopped with an Assert.Fail call +#endif + } + } +} diff --git a/src/BizHawk.Tests.Testroms.GB/ImageUtils.cs b/src/BizHawk.Tests.Testroms.GB/ImageUtils.cs new file mode 100644 index 0000000000..cab0d57f18 --- /dev/null +++ b/src/BizHawk.Tests.Testroms.GB/ImageUtils.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Linq; + +using BizHawk.Common; +using BizHawk.Common.PathExtensions; + +using ImageMagick; + +namespace BizHawk.Tests.Testroms.GB +{ + public static class ImageUtils + { + public static Bitmap AsBitmap(this Image img) + => img as Bitmap ?? new Bitmap(img, img.Size); + + /// w/o leading '.' + private static (string Expect, string Actual, string Glob) GenFilenames((string Suite, string Case) id, string fileExt = "png") + { + var prefix = $"{id.Suite}/{id.Case.GetHashCode():X8}"; // hashcode of string sadly not stable + var suffix = $"{id.Case.RemoveInvalidFileSystemChars().Replace(' ', '_')}.{fileExt}"; + return ($"{prefix}_expect_{suffix}", $"{prefix}_actual_{suffix}", $"{prefix}_*_{suffix}"); + } + + public static int GetRawPixel(this Bitmap b, int x, int y) + => b.GetPixel(x, y).ToArgb() & 0xFFFFFF; + + /// ints are ARGB as + public static Bitmap PaletteSwap(Image img, IReadOnlyDictionary map) + { + int Lookup(int c) + => map.TryGetValue(c, out var c1) ? c1 : c; + var b = ((Image) img.Clone()).AsBitmap(); + for (int y = 0, ly = b.Height; y < ly; y++) for (int x = 0, lx = b.Width; x < lx; x++) + { + b.SetPixel(x, y, Color.FromArgb(0xFF, Color.FromArgb(Lookup(b.GetRawPixel(x, y))))); + } + return b; + } + + public static void PrintPalette(Image imgA, string labelA, Image imgB, string labelB) + { + static IReadOnlySet CollectPalette(Image img) + { + var b = img.AsBitmap(); + HashSet paletteE = new(); + for (int y = 0, ly = b.Height; y < ly; y++) for (int x = 0, lx = b.Width; x < lx; x++) + { + paletteE.Add(b.GetRawPixel(x, y)); + } + return paletteE; + } + static string F(Image img) + => string.Join(", ", CollectPalette(img).Select(static i => $"{i:X6}")); + Console.WriteLine($"palette of {labelA}:\n{F(imgA)}\npalette of {labelB}:\n{F(imgB)}"); + } + + public static void SaveScreenshot(Image img, (string Suite, string Case) id) + { + var filename = GenFilenames(id).Actual; + img.ToMagickImage().Write(filename, MagickFormat.Png); + Console.WriteLine($"screenshot saved for {id.Case} as {filename}"); + } + + /// initially added this as a workaround for various bugs in System.Drawing.* on Linux, but this also happens to be faster on Windows + public static TestUtils.TestSuccessState ScreenshotsEqualMagickDotNET(Stream expectFile, Image actual, bool expectingNotEqual, (string Suite, string Case) id) + { + var actualIM = actual.ToMagickImage(); + MagickImage expectIM = new(expectFile); + var error = expectIM.Compare(actualIM, ErrorMetric.Absolute); + var state = TestUtils.SuccessState(error == 0.0, expectingNotEqual); + if (!SkipFileIO(state)) + { + var (filenameExpect, filenameActual, filenameGlob) = GenFilenames(id); + actualIM.Write(filenameActual, MagickFormat.Png); + expectIM.Write(filenameExpect, MagickFormat.Png); + Console.WriteLine($"screenshots saved for {id.Case} as {filenameGlob} (difference: {error})"); + } + return state; + } + + public static bool SkipFileIO(TestUtils.TestSuccessState state) +#if SAVE_IMAGES_ON_FAIL && SAVE_IMAGES_ON_PASS // run with env. var BIZHAWKTEST_SAVE_IMAGES=all + => false; +#elif SAVE_IMAGES_ON_FAIL // run without extra env. var, or with env. var BIZHAWKTEST_SAVE_IMAGES=failures + => state is TestUtils.TestSuccessState.Success; +#elif SAVE_IMAGES_ON_PASS // normally inaccessible + => state is not TestUtils.TestSuccessState.Success; +#else // run with env. var BIZHAWKTEST_SAVE_IMAGES=none + => true; +#endif + + public static MagickImage ToMagickImage(this Image img) + { + MemoryStream ms = new(); + img.Save(ms, OSTailoredCode.IsUnixHost ? ImageFormat.Bmp : ImageFormat.Png); + ms.Position = 0; + return new(ms); + } + } +} diff --git a/src/BizHawk.Tests.Testroms.GB/Properties/AssemblyInfo.cs b/src/BizHawk.Tests.Testroms.GB/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..025cb0d716 --- /dev/null +++ b/src/BizHawk.Tests.Testroms.GB/Properties/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: TestDataSourceDiscovery(TestDataSourceDiscoveryOption.DuringExecution)] diff --git a/src/BizHawk.Tests.Testroms.GB/TestUtils.cs b/src/BizHawk.Tests.Testroms.GB/TestUtils.cs new file mode 100644 index 0000000000..549ebba4cc --- /dev/null +++ b/src/BizHawk.Tests.Testroms.GB/TestUtils.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; + +using BizHawk.Common; +using BizHawk.Emulation.Common; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace BizHawk.Tests.Testroms.GB +{ + public static class TestUtils + { + public enum TestSuccessState { ExpectedFailure, Failure, Success, UnexpectedSuccess } + + [DllImport("kernel32.dll", SetLastError = true)] + private static extern uint SetDllDirectory(string lpPathName); + + private static readonly SortedSet _initialised = new(); + + public static bool IsKnownFailure(string caseStr, IReadOnlyCollection knownFailures) + => knownFailures is string[] a + ? Array.BinarySearch(a, caseStr) >= 0 + : knownFailures.Contains(caseStr); + + public static void PrepareDBAndOutput(string suiteID) + { + if (_initialised.Contains(suiteID)) return; + if (_initialised.Count == 0) + { + Database.InitializeDatabase(Path.Combine(".", "gamedb", "gamedb.txt"), silent: true); // runs in the background; required for Database.GetGameInfo calls + if (!OSTailoredCode.IsUnixHost) SetDllDirectory(Path.Combine("..", "output", "dll")); // on Linux, this is done by the shell script with the env. var. LD_LIBRARY_PATH + } + _initialised.Add(suiteID); + DirectoryInfo di = new(suiteID); + if (di.Exists) di.Delete(recursive: true); + di.Create(); + } + + [Conditional("SKIP_KNOWN_FAILURES")] // run with env. var BIZHAWKTEST_RUN_KNOWN_FAILURES=1 + public static void ShortCircuitKnownFailure(bool knownFail) + { + if (knownFail) Assert.Inconclusive("short-circuiting this test which is known to fail"); + } + + public static void ShortCircuitKnownFailure(string caseStr, IReadOnlyCollection knownFailures, out bool isKnownFailure) + { + isKnownFailure = IsKnownFailure(caseStr, knownFailures); + ShortCircuitKnownFailure(isKnownFailure); + } + + public static void ShortCircuitMissingRom(bool isPresent) + { + if (!isPresent) Assert.Inconclusive("missing file(s)"); + } + + /// programmatically veto any test cases by modifying this method + public static bool ShouldIgnoreCase(string suiteID, string caseStr) + { +// if (caseStr.Contains("timing")) return true; + return false; + } + + public static TestSuccessState SuccessState(bool didPass, bool shouldNotPass) + => shouldNotPass + ? didPass ? TestSuccessState.UnexpectedSuccess : TestSuccessState.ExpectedFailure + : didPass ? TestSuccessState.Success : TestSuccessState.Failure; + + public static void WriteMetricsToDisk() + => File.WriteAllText("total_frames.txt", $"emulated {DummyFrontend.TotalFrames} frames total"); + } +} diff --git a/src/BizHawk.Tests.Testroms.GB/readme.md b/src/BizHawk.Tests.Testroms.GB/readme.md new file mode 100644 index 0000000000..bd05cf1a8b --- /dev/null +++ b/src/BizHawk.Tests.Testroms.GB/readme.md @@ -0,0 +1,75 @@ +Before building, testroms and firmware need to be placed under `/res` in this project. +You *should* be able to omit any suite or firmware and the relevant cases will be skipped. +Firmware needs to be manually copied into a `fw` dir; +testroms need to be copied into a separate dir per suite, with a hierarchy matching the CI artifacts of [this repo](https://gitlab.com/tasbot/libre-roms-ci). +On Linux, run `/res/download_from_ci.sh` to automatically download and extract said artifacts. +On Windows, run the same script in WSL, or do it manually (because Yoshi can't be bothered porting the script to PowerShell). +All told, the expected directory structure is: +``` +res +├─ BullyGB_artifact +├─ cgb-acid-hell_artifact +├─ cgb-acid2_artifact +├─ dmg-acid2_artifact +├─ fw +│ ├─ GB__World__DMG.bin +│ └─ GBC__World__CGB.bin +├─ mealybug-tearoom-tests_artifact +└─ rtc3test_artifact +``` + +As with EmuHawk, the target framework and configuration for all the BizHawk project deps is dictated by this project. That means .NET Standard 2.0, or .NET 5 if the project supports it. +To build and run the tests in `Release` configuration (or `Debug` if you need that for some reason): +- On Linux, run `run_tests_release.sh` or `run_tests_debug.sh`. +- On Windows, pass `-c Release` to `dotnet test` (must `cd` to this project). Omitting `-c` will use `Debug`. + +> You can at this point run the tests, but you should probably keep reading to see your options. + +To run only some suites, comment out applications of the `[DataTestMethod]` attribute in the source. (Or applications of `[TestClass]`.) +You can also disable individual test cases programmatically by modifying `TestUtils.ShouldIgnoreCase`— +note that "ignored" here means cases are completely removed, and do not count as "skipped". + +By default, known failures are counted as "skipped" *without actually running them*. +Set the env. var `BIZHAWKTEST_RUN_KNOWN_FAILURES=1` to run them as well. They will count as "skipped" if they fail, or "failed" if they succeed unexpectedly. + +On Linux, all cases for unavailable cores (N/A currently) are counted as "skipped". + +Screenshots may be saved under `/test_output/` **in the repo**. +For ease of typing, a random prefix is chosen for each case e.g. `DEADBEEF_{expected,actual}_*.png`. This is included in stdout (Windows users, see below for how to enable stdout). + +The env. var `BIZHAWKTEST_SAVE_IMAGES` determines when to save screenshots (usually an expect/actual pair) to disk. +- With `BIZHAWKTEST_SAVE_IMAGES=all`, all screenshots are saved. +- With `BIZHAWKTEST_SAVE_IMAGES=failures` (the default), only screenshots of failed tests are saved. +- With `BIZHAWKTEST_SAVE_IMAGES=none`, screenshots are never saved. + +Test results are output using the logger(s) specified on the command-line. +(Without the `console` logger, the results are summarised in the console, but prints to stdout are not shown.) +- On Linux, the shell scripts add the `console` and `junit` (to file, for GitLab CI) loggers. +- On Windows, pass `-l "console;verbosity=detailed"` to `dotnet`. + +> Note that the results and stdout for each test case are not printed immediately. +> Cases are grouped by test method, and once the set of test cases is finished executing, the outputs are sent to the console all at once. + +Linux examples: +```sh +# default: simple regression testing, all test suites, saving failures to disk +./run_tests_release.sh + +# every test from every suite, not saving anything to disk (as might be used in CI) +BIZHAWKTEST_RUN_KNOWN_FAILURES=1 BIZHAWKTEST_SAVE_IMAGES=none ./run_tests_release.sh +``` + +Windows examples: +```pwsh +# reminder that if you have WSL, you can use that to run /res/download_from_ci.sh first + +# default: simple regression testing, all test suites, saving failures to disk +dotnet test -c Release -l "console;verbosity=detailed" + +# same as Linux CI example +$Env:BIZHAWKTEST_RUN_KNOWN_FAILURES = 1 +$Env:BIZHAWKTEST_SAVE_IMAGES = "all" +dotnet test -c Release -l "console;verbosity=detailed" +``` + +Summary of `BIZHAWKTEST_RUN_KNOWN_FAILURES=1 ./run_tests_release.sh` should read 86 passed / 118 skipped / 0 failed. diff --git a/src/BizHawk.Tests.Testroms.GB/res/download_from_ci.sh b/src/BizHawk.Tests.Testroms.GB/res/download_from_ci.sh new file mode 100755 index 0000000000..dae2847e8e --- /dev/null +++ b/src/BizHawk.Tests.Testroms.GB/res/download_from_ci.sh @@ -0,0 +1,4 @@ +#!/bin/sh +set -e +cd "$(dirname "$(realpath "$0")")" +../../BizHawk.Tests.Testroms.GB/.download_from_ci.sh BullyGB cgb-acid-hell cgb-acid2 dmg-acid2 mealybug-tearoom-tests rtc3test diff --git a/src/BizHawk.Tests.Testroms.GB/run_tests_debug.sh b/src/BizHawk.Tests.Testroms.GB/run_tests_debug.sh new file mode 100755 index 0000000000..5ec4149a1f --- /dev/null +++ b/src/BizHawk.Tests.Testroms.GB/run_tests_debug.sh @@ -0,0 +1,2 @@ +#!/bin/sh +cd "$(dirname "$(realpath "$0")")" && ../BizHawk.Tests.Testroms.GB/.run_tests_with_configuration.sh "Debug" "$@" diff --git a/src/BizHawk.Tests.Testroms.GB/run_tests_release.sh b/src/BizHawk.Tests.Testroms.GB/run_tests_release.sh new file mode 100755 index 0000000000..be16859693 --- /dev/null +++ b/src/BizHawk.Tests.Testroms.GB/run_tests_release.sh @@ -0,0 +1,2 @@ +#!/bin/sh +cd "$(dirname "$(realpath "$0")")" && ../BizHawk.Tests.Testroms.GB/.run_tests_with_configuration.sh "Release" "$@"