From bdda6b050977fb94fe05e9a9e54ec378918c63d0 Mon Sep 17 00:00:00 2001 From: Matthew Berry Date: Sat, 8 May 2021 22:25:21 -0700 Subject: [PATCH] copied over cryboy tests and made them run again --- spec/gb/cpu_spec.cr | 772 ++++++++++++++++++ spec/gb/cryboy_spec.cr | 4 + spec/gb/joypad_spec.cr | 54 ++ spec/gb/memory_spec.cr | 31 + spec/gb/run_test_roms.cr | 109 +++ spec/gb/spec_helper.cr | 28 + spec/gb/test_results/README.md | 149 ++++ spec/gb/test_results/readme.md | 149 ++++ .../gb/test_results/screenshots/cgb-acid2.png | Bin 0 -> 1940 bytes .../screenshots/cgb-acid2_fifo.png | Bin 0 -> 1940 bytes .../gb/test_results/screenshots/dmg-acid2.png | Bin 0 -> 2027 bytes .../screenshots/dmg-acid2_fifo.png | Bin 0 -> 2027 bytes .../screenshots/expected/cgb-acid2.png | Bin 0 -> 870 bytes .../screenshots/expected/dmg-acid2.png | Bin 0 -> 787 bytes .../screenshots/expected/m2_win_en_toggle.png | Bin 0 -> 718 bytes .../screenshots/expected/m3_bgp_change.png | Bin 0 -> 404 bytes .../expected/m3_bgp_change_sprites.png | Bin 0 -> 794 bytes .../expected/m3_lcdc_bg_en_change.png | Bin 0 -> 791 bytes .../expected/m3_lcdc_bg_en_change2.png | Bin 0 -> 778 bytes .../expected/m3_lcdc_bg_map_change.png | Bin 0 -> 472 bytes .../expected/m3_lcdc_bg_map_change2.png | Bin 0 -> 654 bytes .../expected/m3_lcdc_obj_en_change.png | Bin 0 -> 357 bytes .../m3_lcdc_obj_en_change_variant.png | Bin 0 -> 452 bytes .../expected/m3_lcdc_obj_size_change.png | Bin 0 -> 737 bytes .../expected/m3_lcdc_obj_size_change_scx.png | Bin 0 -> 421 bytes .../expected/m3_lcdc_tile_sel_change.png | Bin 0 -> 506 bytes .../expected/m3_lcdc_tile_sel_change2.png | Bin 0 -> 921 bytes .../expected/m3_lcdc_tile_sel_win_change.png | Bin 0 -> 483 bytes .../expected/m3_lcdc_tile_sel_win_change2.png | Bin 0 -> 860 bytes .../m3_lcdc_win_en_change_multiple.png | Bin 0 -> 362 bytes .../m3_lcdc_win_en_change_multiple_wx.png | Bin 0 -> 636 bytes .../expected/m3_lcdc_win_map_change.png | Bin 0 -> 456 bytes .../expected/m3_lcdc_win_map_change2.png | Bin 0 -> 561 bytes .../screenshots/expected/m3_obp0_change.png | Bin 0 -> 599 bytes .../expected/m3_scx_high_5_bits.png | Bin 0 -> 1014 bytes .../expected/m3_scx_high_5_bits_change2.png | Bin 0 -> 647 bytes .../expected/m3_scx_low_3_bits.png | Bin 0 -> 328 bytes .../screenshots/expected/m3_scy_change.png | Bin 0 -> 2141 bytes .../screenshots/expected/m3_scy_change2.png | Bin 0 -> 760 bytes .../screenshots/expected/m3_window_timing.png | Bin 0 -> 300 bytes .../expected/m3_window_timing_wx_0.png | Bin 0 -> 307 bytes .../screenshots/expected/m3_wx_4_change.png | Bin 0 -> 499 bytes .../expected/m3_wx_4_change_sprites.png | Bin 0 -> 296 bytes .../screenshots/expected/m3_wx_5_change.png | Bin 0 -> 342 bytes .../screenshots/expected/m3_wx_6_change.png | Bin 0 -> 577 bytes .../screenshots/m2_win_en_toggle.png | Bin 0 -> 3538 bytes .../screenshots/m3_bgp_change.png | Bin 0 -> 1104 bytes .../screenshots/m3_bgp_change_sprites.png | Bin 0 -> 2629 bytes .../screenshots/m3_lcdc_bg_en_change.png | Bin 0 -> 5337 bytes .../screenshots/m3_lcdc_bg_en_change2.png | Bin 0 -> 3017 bytes .../screenshots/m3_lcdc_bg_map_change.png | Bin 0 -> 1010 bytes .../screenshots/m3_lcdc_bg_map_change2.png | Bin 0 -> 3017 bytes .../screenshots/m3_lcdc_obj_en_change.png | Bin 0 -> 812 bytes .../m3_lcdc_obj_en_change_variant.png | Bin 0 -> 812 bytes .../screenshots/m3_lcdc_obj_size_change.png | Bin 0 -> 1424 bytes .../m3_lcdc_obj_size_change_scx.png | Bin 0 -> 1191 bytes .../screenshots/m3_lcdc_tile_sel_change.png | Bin 0 -> 1018 bytes .../screenshots/m3_lcdc_tile_sel_change2.png | Bin 0 -> 1640 bytes .../m3_lcdc_tile_sel_win_change.png | Bin 0 -> 1018 bytes .../m3_lcdc_tile_sel_win_change2.png | Bin 0 -> 2030 bytes .../m3_lcdc_win_en_change_multiple.png | Bin 0 -> 5295 bytes .../m3_lcdc_win_en_change_multiple_wx.png | Bin 0 -> 1042 bytes .../screenshots/m3_lcdc_win_map_change.png | Bin 0 -> 1010 bytes .../screenshots/m3_lcdc_win_map_change2.png | Bin 0 -> 1600 bytes .../screenshots/m3_obp0_change.png | Bin 0 -> 841 bytes .../screenshots/m3_scx_high_5_bits.png | Bin 0 -> 5570 bytes .../m3_scx_high_5_bits_change2.png | Bin 0 -> 2934 bytes .../screenshots/m3_scx_low_3_bits.png | Bin 0 -> 670 bytes .../screenshots/m3_scy_change.png | Bin 0 -> 6725 bytes .../screenshots/m3_scy_change2.png | Bin 0 -> 3068 bytes .../screenshots/m3_window_timing.png | Bin 0 -> 930 bytes .../screenshots/m3_window_timing_wx_0.png | Bin 0 -> 1326 bytes .../screenshots/m3_wx_4_change.png | Bin 0 -> 5367 bytes .../screenshots/m3_wx_4_change_sprites.png | Bin 0 -> 574 bytes .../screenshots/m3_wx_5_change.png | Bin 0 -> 1195 bytes .../screenshots/m3_wx_6_change.png | Bin 0 -> 1216 bytes spec/gb/util_spec.cr | 17 + 77 files changed, 1313 insertions(+) create mode 100644 spec/gb/cpu_spec.cr create mode 100644 spec/gb/cryboy_spec.cr create mode 100644 spec/gb/joypad_spec.cr create mode 100644 spec/gb/memory_spec.cr create mode 100644 spec/gb/run_test_roms.cr create mode 100644 spec/gb/spec_helper.cr create mode 100644 spec/gb/test_results/README.md create mode 100644 spec/gb/test_results/readme.md create mode 100644 spec/gb/test_results/screenshots/cgb-acid2.png create mode 100644 spec/gb/test_results/screenshots/cgb-acid2_fifo.png create mode 100644 spec/gb/test_results/screenshots/dmg-acid2.png create mode 100644 spec/gb/test_results/screenshots/dmg-acid2_fifo.png create mode 100644 spec/gb/test_results/screenshots/expected/cgb-acid2.png create mode 100644 spec/gb/test_results/screenshots/expected/dmg-acid2.png create mode 100644 spec/gb/test_results/screenshots/expected/m2_win_en_toggle.png create mode 100644 spec/gb/test_results/screenshots/expected/m3_bgp_change.png create mode 100644 spec/gb/test_results/screenshots/expected/m3_bgp_change_sprites.png create mode 100644 spec/gb/test_results/screenshots/expected/m3_lcdc_bg_en_change.png create mode 100644 spec/gb/test_results/screenshots/expected/m3_lcdc_bg_en_change2.png create mode 100644 spec/gb/test_results/screenshots/expected/m3_lcdc_bg_map_change.png create mode 100644 spec/gb/test_results/screenshots/expected/m3_lcdc_bg_map_change2.png create mode 100644 spec/gb/test_results/screenshots/expected/m3_lcdc_obj_en_change.png create mode 100644 spec/gb/test_results/screenshots/expected/m3_lcdc_obj_en_change_variant.png create mode 100644 spec/gb/test_results/screenshots/expected/m3_lcdc_obj_size_change.png create mode 100644 spec/gb/test_results/screenshots/expected/m3_lcdc_obj_size_change_scx.png create mode 100644 spec/gb/test_results/screenshots/expected/m3_lcdc_tile_sel_change.png create mode 100644 spec/gb/test_results/screenshots/expected/m3_lcdc_tile_sel_change2.png create mode 100644 spec/gb/test_results/screenshots/expected/m3_lcdc_tile_sel_win_change.png create mode 100644 spec/gb/test_results/screenshots/expected/m3_lcdc_tile_sel_win_change2.png create mode 100644 spec/gb/test_results/screenshots/expected/m3_lcdc_win_en_change_multiple.png create mode 100644 spec/gb/test_results/screenshots/expected/m3_lcdc_win_en_change_multiple_wx.png create mode 100644 spec/gb/test_results/screenshots/expected/m3_lcdc_win_map_change.png create mode 100644 spec/gb/test_results/screenshots/expected/m3_lcdc_win_map_change2.png create mode 100644 spec/gb/test_results/screenshots/expected/m3_obp0_change.png create mode 100644 spec/gb/test_results/screenshots/expected/m3_scx_high_5_bits.png create mode 100644 spec/gb/test_results/screenshots/expected/m3_scx_high_5_bits_change2.png create mode 100644 spec/gb/test_results/screenshots/expected/m3_scx_low_3_bits.png create mode 100644 spec/gb/test_results/screenshots/expected/m3_scy_change.png create mode 100644 spec/gb/test_results/screenshots/expected/m3_scy_change2.png create mode 100644 spec/gb/test_results/screenshots/expected/m3_window_timing.png create mode 100644 spec/gb/test_results/screenshots/expected/m3_window_timing_wx_0.png create mode 100644 spec/gb/test_results/screenshots/expected/m3_wx_4_change.png create mode 100644 spec/gb/test_results/screenshots/expected/m3_wx_4_change_sprites.png create mode 100644 spec/gb/test_results/screenshots/expected/m3_wx_5_change.png create mode 100644 spec/gb/test_results/screenshots/expected/m3_wx_6_change.png create mode 100644 spec/gb/test_results/screenshots/m2_win_en_toggle.png create mode 100644 spec/gb/test_results/screenshots/m3_bgp_change.png create mode 100644 spec/gb/test_results/screenshots/m3_bgp_change_sprites.png create mode 100644 spec/gb/test_results/screenshots/m3_lcdc_bg_en_change.png create mode 100644 spec/gb/test_results/screenshots/m3_lcdc_bg_en_change2.png create mode 100644 spec/gb/test_results/screenshots/m3_lcdc_bg_map_change.png create mode 100644 spec/gb/test_results/screenshots/m3_lcdc_bg_map_change2.png create mode 100644 spec/gb/test_results/screenshots/m3_lcdc_obj_en_change.png create mode 100644 spec/gb/test_results/screenshots/m3_lcdc_obj_en_change_variant.png create mode 100644 spec/gb/test_results/screenshots/m3_lcdc_obj_size_change.png create mode 100644 spec/gb/test_results/screenshots/m3_lcdc_obj_size_change_scx.png create mode 100644 spec/gb/test_results/screenshots/m3_lcdc_tile_sel_change.png create mode 100644 spec/gb/test_results/screenshots/m3_lcdc_tile_sel_change2.png create mode 100644 spec/gb/test_results/screenshots/m3_lcdc_tile_sel_win_change.png create mode 100644 spec/gb/test_results/screenshots/m3_lcdc_tile_sel_win_change2.png create mode 100644 spec/gb/test_results/screenshots/m3_lcdc_win_en_change_multiple.png create mode 100644 spec/gb/test_results/screenshots/m3_lcdc_win_en_change_multiple_wx.png create mode 100644 spec/gb/test_results/screenshots/m3_lcdc_win_map_change.png create mode 100644 spec/gb/test_results/screenshots/m3_lcdc_win_map_change2.png create mode 100644 spec/gb/test_results/screenshots/m3_obp0_change.png create mode 100644 spec/gb/test_results/screenshots/m3_scx_high_5_bits.png create mode 100644 spec/gb/test_results/screenshots/m3_scx_high_5_bits_change2.png create mode 100644 spec/gb/test_results/screenshots/m3_scx_low_3_bits.png create mode 100644 spec/gb/test_results/screenshots/m3_scy_change.png create mode 100644 spec/gb/test_results/screenshots/m3_scy_change2.png create mode 100644 spec/gb/test_results/screenshots/m3_window_timing.png create mode 100644 spec/gb/test_results/screenshots/m3_window_timing_wx_0.png create mode 100644 spec/gb/test_results/screenshots/m3_wx_4_change.png create mode 100644 spec/gb/test_results/screenshots/m3_wx_4_change_sprites.png create mode 100644 spec/gb/test_results/screenshots/m3_wx_5_change.png create mode 100644 spec/gb/test_results/screenshots/m3_wx_6_change.png create mode 100644 spec/gb/util_spec.cr diff --git a/spec/gb/cpu_spec.cr b/spec/gb/cpu_spec.cr new file mode 100644 index 0000000..2633633 --- /dev/null +++ b/spec/gb/cpu_spec.cr @@ -0,0 +1,772 @@ +require "./spec_helper" + +describe CPU do + describe "registers" do + it "do computations correctly across registers" do + cpu = new_cpu [] of UInt8 + cpu.b = 0x00 + cpu.c = 0x00 + cpu.bc.should eq 0x0000 + cpu.c += 0x01 + cpu.b.should eq 0x00 + cpu.c.should eq 0x01 + cpu.bc.should eq 0x0001 + cpu.bc += 0x4320 + cpu.b.should eq 0x43 + cpu.c.should eq 0x21 + cpu.bc.should eq 0x4321 + end + end + + describe "unprefixed opcode" do + describe "0x00" do + it "does nothing" do + cpu = new_cpu [0x00] + cpu.tick + + cpu.pc.should eq 1 + cpu.sp.should eq 0xFFFE + end + end + + describe "0x01" do + it "loads bc with d16" do + d16 = 0x1234 + cpu = new_cpu [0x01, d16 & 0xFF, d16 >> 8] + cpu.tick + + cpu.pc.should eq 3 + cpu.sp.should eq 0xFFFE + cpu.bc.should eq d16 + end + end + + describe "0x02" do + it "loads (bc) with a" do + cpu = new_cpu [0x02] + cpu.a = 0x34 + cpu.bc = 0xA000 + cpu.tick + + cpu.pc.should eq 1 + cpu.sp.should eq 0xFFFE + cpu.memory[0xA000].should eq 0x34 + end + end + + describe "0x03" do + it "increments bc" do + cpu = new_cpu [0x03] + cpu.bc = 0x1234 + cpu.tick + + cpu.pc.should eq 1 + cpu.sp.should eq 0xFFFE + cpu.bc.should eq 0x1235 + end + end + + describe "0x04" do + it "increments b" do + cpu = new_cpu [0x04] + cpu.b = 0x12 + cpu.tick + + cpu.pc.should eq 1 + cpu.sp.should eq 0xFFFE + cpu.b.should eq 0x13 + end + end + + describe "0x05" do + it "decrements b" do + cpu = new_cpu [0x05] + cpu.b = 0x12 + cpu.tick + + cpu.pc.should eq 1 + cpu.sp.should eq 0xFFFE + cpu.b.should eq 0x11 + end + end + + describe "0x06" do + it "loads b with d8" do + d8 = 0x12 + cpu = new_cpu [0x06, d8] + cpu.tick + + cpu.pc.should eq 2 + cpu.sp.should eq 0xFFFE + cpu.b.should eq d8 + end + end + + describe "0x07" do + it "rotates accumulator left w/o carry" do + cpu = new_cpu [0x07] + cpu.a = 0b01011010 + cpu.tick + + cpu.pc.should eq 1 + cpu.sp.should eq 0xFFFE + cpu.a.should eq 0b10110100 + cpu.f_c.should eq false + end + + it "rotates accumulator left w/ carry" do + cpu = new_cpu [0x07] + cpu.a = 0b10100101 + cpu.tick + + cpu.pc.should eq 1 + cpu.sp.should eq 0xFFFE + cpu.a.should eq 0b01001011 + cpu.f_c.should eq true + end + end + + describe "0x08" do + it "loads (d16) with sp" do + d16 = 0xA000 + cpu = new_cpu [0x08, d16 & 0xFF, d16 >> 8] + cpu.tick + + cpu.pc.should eq 3 + cpu.sp.should eq 0xFFFE + cpu.memory[0xA000] = 0xFFFE + end + end + + describe "0x09" do + it "adds bc to hl" do + cpu = new_cpu [0x09] + cpu.hl = 0x1010 + cpu.bc = 0x1111 + cpu.tick + + cpu.pc.should eq 1 + cpu.sp.should eq 0xFFFE + cpu.hl.should eq 0x2121 + cpu.bc.should eq 0x1111 + end + end + + describe "0x0A" do + it "loads a with (bc)" do + cpu = new_cpu [0x0A, 0x12] + cpu.bc = 0x0001_u8 + cpu.tick + + cpu.pc.should eq 1 + cpu.sp.should eq 0xFFFE + cpu.bc.should eq 0x0001 + cpu.memory[0x01].should eq 0x12 + end + end + + describe "0x0B" do + it "decrememnts bc" do + cpu = new_cpu [0x0B] + cpu.bc = 0x1234 + cpu.tick + + cpu.pc.should eq 1 + cpu.sp.should eq 0xFFFE + cpu.bc.should eq 0x1233 + end + end + + describe "0x0C" do + it "increments c" do + cpu = new_cpu [0x0C] + cpu.c = 0x12 + cpu.tick + + cpu.pc.should eq 1 + cpu.sp.should eq 0xFFFE + cpu.c.should eq 0x13 + end + end + + describe "0x0D" do + it "decrements c" do + cpu = new_cpu [0x0D] + cpu.c = 0x12 + cpu.tick + + cpu.pc.should eq 1 + cpu.sp.should eq 0xFFFE + cpu.c.should eq 0x11 + end + end + + describe "0x0E" do + it "loads c with d8" do + cpu = new_cpu [0x0E, 0x12] + cpu.tick + + cpu.pc.should eq 2 + cpu.sp.should eq 0xFFFE + cpu.c.should eq 0x12 + end + end + + describe "0x0F" do + it "rotates accumulator right w/o carry" do + cpu = new_cpu [0x0F] + cpu.a = 0b01011010 + cpu.tick + + cpu.pc.should eq 1 + cpu.sp.should eq 0xFFFE + cpu.a.should eq 0b00101101 + cpu.f_c.should eq false + end + + it "rotates accumulator right w/ carry" do + cpu = new_cpu [0x0F] + cpu.a = 0b10100101 + cpu.tick + + cpu.pc.should eq 1 + cpu.sp.should eq 0xFFFE + cpu.a.should eq 0b11010010 + cpu.f_c.should eq true + end + end + + describe "0x10" do + it "stops execution" do + # todo: implement and test + end + end + + describe "0x11" do + it "loads de with d16" do + d16 = 0x1234 + cpu = new_cpu [0x11, d16 & 0xFF, d16 >> 8] + cpu.tick + + cpu.pc.should eq 3 + cpu.sp.should eq 0xFFFE + cpu.de.should eq d16 + end + end + + describe "0x12" do + it "loads (de) with a" do + cpu = new_cpu [0x12] + cpu.a = 0x34 + cpu.de = 0xA000 + cpu.tick + + cpu.pc.should eq 1 + cpu.sp.should eq 0xFFFE + cpu.memory[0xA000].should eq 0x34 + end + end + + describe "0x13" do + it "increments de" do + cpu = new_cpu [0x13] + cpu.de = 0x1234 + cpu.tick + + cpu.pc.should eq 1 + cpu.sp.should eq 0xFFFE + cpu.de.should eq 0x1235 + end + end + + describe "0x14" do + it "increments d" do + cpu = new_cpu [0x14] + cpu.d = 0x12 + cpu.tick + + cpu.pc.should eq 1 + cpu.sp.should eq 0xFFFE + cpu.d.should eq 0x13 + end + end + + describe "0x15" do + it "decrements d" do + cpu = new_cpu [0x15] + cpu.d = 0x12 + cpu.tick + + cpu.pc.should eq 1 + cpu.sp.should eq 0xFFFE + cpu.d.should eq 0x11 + end + end + + describe "0x16" do + it "loads d with d8" do + d8 = 0x12 + cpu = new_cpu [0x16, d8] + cpu.tick + + cpu.pc.should eq 2 + cpu.sp.should eq 0xFFFE + cpu.d.should eq d8 + end + end + + describe "0x17" do + it "rotates accumulator left through carry w/o carry" do + cpu = new_cpu [0x17] + cpu.a = 0b01011010 + cpu.f_c = true + cpu.tick + + cpu.pc.should eq 1 + cpu.sp.should eq 0xFFFE + cpu.a.should eq 0b10110101 + cpu.f_c.should eq false + end + + it "rotates accumulator left through carry w/ carry" do + cpu = new_cpu [0x17] + cpu.a = 0b10100101 + cpu.f_c = false + cpu.tick + + cpu.pc.should eq 1 + cpu.sp.should eq 0xFFFE + cpu.a.should eq 0b01001010 + cpu.f_c.should eq true + end + end + + # todo codes here + + describe "0x21" do + it "loads hl with d16" do + d16 = 0x1234 + cpu = new_cpu [0x21, d16 & 0xFF, d16 >> 8] + cpu.tick + + cpu.pc.should eq 3 + cpu.sp.should eq 0xFFFE + cpu.hl.should eq d16 + end + end + + describe "0x22" do + it "loads (hl+) with a" do + cpu = new_cpu [0x22] + cpu.a = 0x34 + cpu.hl = 0xA000 + cpu.tick + + cpu.pc.should eq 1 + cpu.sp.should eq 0xFFFE + cpu.memory[0xA000].should eq 0x34 + cpu.hl.should eq 0xA001 + end + end + + describe "0x23" do + it "increments hl" do + cpu = new_cpu [0x23] + cpu.hl = 0x1234 + cpu.tick + + cpu.pc.should eq 1 + cpu.sp.should eq 0xFFFE + cpu.hl.should eq 0x1235 + end + end + + describe "0x24" do + it "increments h" do + cpu = new_cpu [0x24] + cpu.h = 0x12 + cpu.tick + + cpu.pc.should eq 1 + cpu.sp.should eq 0xFFFE + cpu.h.should eq 0x13 + end + end + + describe "0x25" do + it "decrements h" do + cpu = new_cpu [0x25] + cpu.h = 0x12 + cpu.tick + + cpu.pc.should eq 1 + cpu.sp.should eq 0xFFFE + cpu.h.should eq 0x11 + end + end + + describe "0x26" do + it "loads h with d8" do + d8 = 0x12 + cpu = new_cpu [0x26, d8] + cpu.tick + + cpu.pc.should eq 2 + cpu.sp.should eq 0xFFFE + cpu.h.should eq d8 + end + end + + # todo codes here + + describe "0x31" do + it "loads sp with d16" do + d16 = 0x1234 + cpu = new_cpu [0x31, d16 & 0xFF, d16 >> 8] + cpu.tick + + cpu.pc.should eq 3 + cpu.sp.should eq d16 + end + end + + describe "0x32" do + it "loads (hl-) with a" do + cpu = new_cpu [0x32] + cpu.a = 0x34 + cpu.hl = 0xA000 + cpu.tick + + cpu.pc.should eq 1 + cpu.sp.should eq 0xFFFE + cpu.memory[0xA000].should eq 0x34 + cpu.hl.should eq 0x9FFF + end + end + + describe "0x33" do + it "increments sp" do + cpu = new_cpu [0x33] + cpu.tick + + cpu.pc.should eq 1 + cpu.sp.should eq 0xFFFF + end + end + + describe "0x34" do + it "increments (hl)" do + cpu = new_cpu [0x34] + cpu.memory[0xA000] = 0x12_u8 + cpu.hl = 0xA000 + cpu.tick + + cpu.pc.should eq 1 + cpu.sp.should eq 0xFFFE + cpu.memory[0xA000].should eq 0x13 + end + end + + describe "0x35" do + it "decrements (hl)" do + cpu = new_cpu [0x35] + cpu.memory[0xA000] = 0x12_u8 + cpu.hl = 0xA000 + cpu.tick + + cpu.pc.should eq 1 + cpu.sp.should eq 0xFFFE + cpu.memory[0xA000].should eq 0x11 + end + end + + describe "0x36" do + it "loads (hl) with d8" do + d8 = 0x12 + cpu = new_cpu [0x36, d8] + cpu.hl = 0xA000 + cpu.tick + + cpu.pc.should eq 2 + cpu.sp.should eq 0xFFFE + cpu.memory[0xA000].should eq d8 + end + end + + # todo codes here + + describe "0xC0" do + it "returns if nz" do + cpu = new_cpu [0xC0] + cpu.sp = 0xFFF0_u16 + cpu.memory[0xFFF0] = 0x1234_u16 + cpu.f_z = false + cpu.tick + + cpu.pc.should eq 0x1234 + cpu.sp.should eq 0xFFF2 + end + + it "doesn't return if not nz" do + cpu = new_cpu [0xC0] + cpu.f_z = true + cpu.tick + + cpu.pc.should eq 1 + cpu.sp.should eq 0xFFFE + end + end + + describe "0xC1" do + it "pops bc" do + cpu = new_cpu [0xC1] + cpu.sp = 0xFFF0_u16 + cpu.memory[0xFFF0_u16] = 0x1234 + cpu.tick + + cpu.pc.should eq 1 + cpu.sp.should eq 0xFFF2 + cpu.bc.should eq 0x1234 + end + end + + describe "0xC2" do + it "jumps to a16 if nz" do + a16 = 0xA000 + cpu = new_cpu [0xC2, a16 & 0xFF, a16 >> 8] + cpu.f_z = false + cpu.tick + + cpu.pc.should eq a16 + cpu.sp.should eq 0xFFFE + end + + it "doesn't jump to a16 if not nz" do + a16 = 0xA000 + cpu = new_cpu [0xC2, a16 & 0xFF, a16 >> 8] + cpu.f_z = true + cpu.tick + + cpu.pc.should eq 3 + cpu.sp.should eq 0xFFFE + end + end + + describe "0xC3" do + it "jumps to a16" do + a16 = 0xA000 + cpu = new_cpu [0xC3, a16 & 0xFF, a16 >> 8] + cpu.tick + + cpu.pc.should eq 0xA000 + cpu.sp.should eq 0xFFFE + end + + it "jumps to a16 regardless of nz" do + a16 = 0xAC00 + cpu = new_cpu [0xC3, a16 & 0xFF, a16 >> 8] + cpu.f_z = false + cpu.tick + cpu.pc.should eq a16 + cpu = new_cpu [0xC3, a16 & 0xFF, a16 >> 8] + cpu.f_z = true + cpu.tick + cpu.pc.should eq a16 + end + end + + describe "0xC4" do + it "calls a16 if nz" do + a16 = 0xAC00 + cpu = new_cpu [0xC4, a16 & 0xFF, a16 >> 8] + cpu.f_z = false + cpu.tick + + cpu.pc.should eq 0xAC00 + cpu.sp.should eq 0xFFFC + cpu.memory[0xFFFD].should eq 0x00 + cpu.memory[0xFFFC].should eq 0x03 + end + + it "doesn't call a16 if not nz" do + a16 = 0xAC00 + cpu = new_cpu [0xC4, a16 & 0xFF, a16 >> 8] + cpu.f_z = true + cpu.tick + + cpu.pc.should eq 3 + cpu.sp.should eq 0xFFFE + end + end + + describe "0xC5" do + it "pushes bc" do + cpu = new_cpu [0xC5] + cpu.bc = 0x1234_u16 + cpu.tick + + cpu.pc.should eq 1 + cpu.sp.should eq 0xFFFC + cpu.memory[0xFFFD].should eq 0x12 + cpu.memory[0xFFFC].should eq 0x34 + end + end + + describe "0xC6" do + it "adds d8 to a" do + d8 = 0x01 + cpu = new_cpu [0xC6, d8] + cpu.a = 0xFF + cpu.tick + + cpu.pc.should eq 2 + cpu.sp.should eq 0xFFFE + cpu.a.should eq 0x00 + cpu.f_z.should eq true + cpu.f_n.should eq false + cpu.f_h.should eq true + cpu.f_c.should eq true + end + end + + # todo codes here + + describe "0xD0" do + it "returns if nc" do + cpu = new_cpu [0xD0] + cpu.sp = 0xFFF0_u16 + cpu.memory[0xFFF0] = 0x1234_u16 + cpu.f_c = false + cpu.tick + + cpu.pc.should eq 0x1234 + cpu.sp.should eq 0xFFF2 + end + + it "doesn't return if not nc" do + cpu = new_cpu [0xD0] + cpu.f_c = true + cpu.tick + + cpu.pc.should eq 1 + cpu.sp.should eq 0xFFFE + end + end + + describe "0xD1" do + it "pops de" do + cpu = new_cpu [0xD1] + cpu.sp = 0xFFF0_u16 + cpu.memory[0xFFF0_u16] = 0x1234 + cpu.tick + + cpu.pc.should eq 1 + cpu.sp.should eq 0xFFF2 + cpu.de.should eq 0x1234 + end + end + + describe "0xD2" do + it "jumps to a16 if nc" do + a16 = 0xA000 + cpu = new_cpu [0xD2, a16 & 0xFF, a16 >> 8] + cpu.f_c = false + cpu.tick + + cpu.pc.should eq a16 + cpu.sp.should eq 0xFFFE + end + + it "doesn't jump to a16 if not nc" do + a16 = 0xA000 + cpu = new_cpu [0xD2, a16 & 0xFF, a16 >> 8] + cpu.f_c = true + cpu.tick + + cpu.pc.should eq 3 + cpu.sp.should eq 0xFFFE + end + end + + describe "0xD3" do + # unused opcode + end + + describe "0xD4" do + it "calls a16 if nc" do + a16 = 0xAC00 + cpu = new_cpu [0xD4, a16 & 0xFF, a16 >> 8] + cpu.f_c = false + cpu.tick + + cpu.pc.should eq 0xAC00 + cpu.sp.should eq 0xFFFC + cpu.memory[0xFFFD].should eq 0x00 + cpu.memory[0xFFFC].should eq 0x03 + end + + it "doesn't call a16 if not nc" do + a16 = 0xAC00 + cpu = new_cpu [0xD4, a16 & 0xFF, a16 >> 8] + cpu.f_c = true + cpu.tick + + cpu.pc.should eq 3 + cpu.sp.should eq 0xFFFE + end + end + + describe "0xD5" do + it "pushes de" do + cpu = new_cpu [0xD5] + cpu.de = 0x1234_u16 + cpu.tick + + cpu.pc.should eq 1 + cpu.sp.should eq 0xFFFC + cpu.memory[0xFFFD].should eq 0x12 + cpu.memory[0xFFFC].should eq 0x34 + end + end + + describe "0xD6" do + it "subs d8 from a" do + d8 = 0x01 + cpu = new_cpu [0xD6, d8] + cpu.a = 0x10 + cpu.tick + + cpu.pc.should eq 2 + cpu.sp.should eq 0xFFFE + cpu.a.should eq 0x0F + cpu.f_z.should eq false + cpu.f_n.should eq true + cpu.f_h.should eq true + cpu.f_c.should eq false + end + end + + # todo codes here + end + + describe "prefixed opcode" do + # todo codes here + + describe "0x40" do + it "tests bit 0 of b" do + cpu = new_cpu [0xCB, 0x40] + cpu.b = 0b01010101 + cpu.tick + + cpu.pc.should eq 2 + cpu.sp.should eq 0xFFFE + cpu.b.should eq 0b01010101 + cpu.f_z.should eq false + cpu.f_c.should eq false + cpu.f_h.should eq true + end + end + + # todo codes here + end +end diff --git a/spec/gb/cryboy_spec.cr b/spec/gb/cryboy_spec.cr new file mode 100644 index 0000000..db95d13 --- /dev/null +++ b/spec/gb/cryboy_spec.cr @@ -0,0 +1,4 @@ +require "./spec_helper" + +describe Crab do +end diff --git a/spec/gb/joypad_spec.cr b/spec/gb/joypad_spec.cr new file mode 100644 index 0000000..2abc4c9 --- /dev/null +++ b/spec/gb/joypad_spec.cr @@ -0,0 +1,54 @@ +describe Joypad do + it "defaults to buttons not pressed" do + joypad = Joypad.new + joypad.read.should eq 0b00111111 + end + + it "toggles button select" do + joypad = Joypad.new + joypad.write 0b00011111 + joypad.read.should eq 0b00011111 + joypad.write 0b00111111 + joypad.read.should eq 0b00111111 + end + + it "toggles direction select" do + joypad = Joypad.new + joypad.write 0b00101111 + joypad.read.should eq 0b00101111 + joypad.write 0b00111111 + joypad.read.should eq 0b00111111 + end + + it "only allows writing to selection keys" do + joypad = Joypad.new + joypad.write 0b00000000 + joypad.read.should eq 0b00001111 + joypad.write 0b11111111 + joypad.read.should eq 0b00111111 + end + + it "sets correct bits for one key down" do + joypad = Joypad.new + joypad.down = true + joypad.write 0b00100000 + joypad.read.should eq 0b00100111 + end + + it "sets correct bits for two different keys down" do + joypad = Joypad.new + joypad.down = true + joypad.b = true + joypad.write 0b00000000 + joypad.read.should eq 0b00000101 + end + + it "sets correct bits for two parallel keys down" do + joypad = Joypad.new + joypad.up = true + joypad.select = true + joypad.button_keys = true + joypad.direction_keys = true + joypad.read.should eq 0b00001011 + end +end diff --git a/spec/gb/memory_spec.cr b/spec/gb/memory_spec.cr new file mode 100644 index 0000000..404a40a --- /dev/null +++ b/spec/gb/memory_spec.cr @@ -0,0 +1,31 @@ +require "./spec_helper" + +describe GB::Memory do + it "can't write over rom" do + bytes = Array.new 0x8000, 0 + bytes[0] = 0x01 + bytes[1] = 0x02 + bytes[2] = 0x03 + memory = new_memory bytes + memory[0x0000] = 0x05_u8 + memory[0x0001] = 0x06_u8 + memory[0x3FFF] = 0x07_u8 + memory[0x4000] = 0x07_u8 + memory[0x7FFF] = 0x08_u8 + + memory[0x0000].should eq 0x01 + memory[0x0001].should eq 0x02 + memory[0x0002].should eq 0x03 + memory[0x3FFF].should eq 0x00 + memory[0x4000].should eq 0x00 + memory[0x7FFF].should eq 0x00 + end + + it "writes to external ram simple" do + memory = new_memory [0x00] + memory[0xA000] = 0x12.to_u8 + memory[0xBFFF] = 0x34.to_u8 + memory[0xA000].should eq 0x12 + memory[0xBFFF].should eq 0x34 + end +end diff --git a/spec/gb/run_test_roms.cr b/spec/gb/run_test_roms.cr new file mode 100644 index 0000000..6d76026 --- /dev/null +++ b/spec/gb/run_test_roms.cr @@ -0,0 +1,109 @@ +require "option_parser" + +TEST_RESULTS_DIR = "spec/gb/test_results" +SCREENSHOT_DIR = "#{TEST_RESULTS_DIR}/screenshots" +README_FILE = "#{TEST_RESULTS_DIR}/README.md" + +# kill process after given number of seconds +def kill(process : Process, after : Number = 5) : Nil + spawn do + sleep after + process.signal Signal::KILL if process.exists? + end +end + +def get_test_name(dir : String, test : String) : String + test.rpartition('.')[0][dir.size + 1, test.size] +end + +alias TestResult = NamedTuple(test: String, pass: Bool) +alias TestSuite = NamedTuple(suite: String, results: Array(TestResult)) + +test_results : Array(TestSuite) = [] of TestSuite + +acid_dir = "" +blargg_dir = "" +mealybug_dir = "" +mooneye_dir = "" + +OptionParser.parse do |parser| + parser.on("--acid PATH", "Path to directory with acid tests") { |path| acid_dir = path } + parser.on("--blargg PATH", "Path to directory with blargg tests") { |path| blargg = path } + parser.on("--mealybug PATH", "Path to directory with mealybug tests") { |path| mealybug_dir = path } + parser.on("--mooneye PATH", "Path to directory with mooneye tests") { |path| mooneye_dir = path } + parser.invalid_option { abort parser } +end + +system "shards build -Dgraphics_test > /dev/null" + +unless acid_dir == "" + [true, false].each do |fifo| + test_results << {suite: "Acid#{" Fifo" if fifo}", results: [] of TestResult} + puts "Acid #{"Fifo " if fifo}Tests" + Dir.glob("#{acid_dir}/*acid2.gb*").sort.each do |path| + test_name = get_test_name acid_dir, path + Process.run "bin/crab", [path, "--headless"] + (fifo ? ["--fifo"] : [] of String) do |process| + kill process, after: 1 + end + system %[touch out.png] # touch image in case something went wrong + system %[mv out.png #{SCREENSHOT_DIR}/#{test_name}#{"_fifo" if fifo}.png] + system %[compare -metric AE #{SCREENSHOT_DIR}/#{test_name}#{"_fifo" if fifo}.png #{SCREENSHOT_DIR}/expected/#{test_name}.png /tmp/crab_diff 2>/dev/null] + passed = $?.exit_status == 0 + test_results[test_results.size - 1][:results] << {test: test_name, pass: passed} + print passed ? "." : "F" + end + print "\n" + end +end + +unless mealybug_dir == "" + test_results << {suite: "Mealybug Fifo", results: [] of TestResult} + puts "Mealybug Fifo Tests" + Dir.glob("#{mealybug_dir}/**/*.gb").sort.each do |path| + test_name = get_test_name mealybug_dir, path + Process.run "bin/crab", [path, "--headless", "--fifo"] do |process| + kill process, after: 1 + end + system %[touch out.png] # touch image in case something went wrong + system %[mv out.png #{SCREENSHOT_DIR}/#{test_name}.png] + system %[compare -metric AE #{SCREENSHOT_DIR}/#{test_name}.png #{SCREENSHOT_DIR}/expected/#{test_name}.png /tmp/crab_diff 2>/dev/null] + passed = $?.exit_status == 0 + test_results[test_results.size - 1][:results] << {test: test_name, pass: passed} + print passed ? "." : "F" + end + print "\n" +end + +system "shards build --release -Dprint_serial > /dev/null" + +unless mooneye_dir == "" + test_results << {suite: "Mooneye", results: [] of TestResult} + puts "Mooneye Tests" + fib_string = "358132134" + Dir.glob("#{mooneye_dir}/**/*.gb").sort.each do |path| + next if path.includes?("util") || path.includes?("manual-only") || path.includes?("dmg") || path.includes?("mgb") || path.includes?("sgb") + test_name = get_test_name mooneye_dir, path + passed = false + Process.run("bin/crab", [path, "--headless"]) do |process| + kill process, after: 10 # seconds + result = process.output.gets 9 + process.terminate if process.exists? + passed = result == fib_string + end + test_results[test_results.size - 1][:results] << {test: test_name, pass: passed} + print passed ? "." : "F" + end + print "\n" +end + +File.open README_FILE, "w" do |file| + file.puts "# Test Results" + test_results.each do |test_suite| + file.puts "## #{test_suite[:suite]} Tests" + file.puts "| Result | Test Name |" + file.puts "|--------|-----------|" + test_suite[:results].each do |test_result| + file.puts "| #{test_result[:pass] ? "👌" : "👀"} | #{test_result[:test]} |" + end + end +end diff --git a/spec/gb/spec_helper.cr b/spec/gb/spec_helper.cr new file mode 100644 index 0000000..eacae48 --- /dev/null +++ b/spec/gb/spec_helper.cr @@ -0,0 +1,28 @@ +# define a new CPU with the given bytes as rom +def new_cpu(bytes : Array(Int), cgb_enabled = true, boot = false) + interrupts = Interrupts.new + display = Display.new + ppu = PPU.new display, interrupts, pointerof(cgb_enabled) + apu = APU.new + timer = Timer.new interrupts + + cpu = CPU.new new_memory(bytes), interrupts, ppu, apu, timer, boot + cpu.sp = 0xFFFE_u16 + cpu +end + +# define a new Memory with the given bytes as rom +def new_memory(bytes : Array(Int), cgb_enabled = true, bootrom = nil) + rom = Bytes.new 0x8000 + bytes.each_with_index do |byte, i| + rom[i] = byte.to_u8! + end + cartridge = GB::Cartridge.new rom + interrupts = GB::Interrupts.new + display = GB::Display.new + ppu = GB::PPU.new display, interrupts, pointerof(cgb_enabled) + apu = GB::APU.new + timer = GB::Timer.new interrupts + joypad = GB::Joypad.new + GB::Memory.new cartridge, interrupts, ppu, apu, timer, joypad, pointerof(cgb_enabled), bootrom +end diff --git a/spec/gb/test_results/README.md b/spec/gb/test_results/README.md new file mode 100644 index 0000000..15d50ef --- /dev/null +++ b/spec/gb/test_results/README.md @@ -0,0 +1,149 @@ +# Test Results +## Acid Fifo Tests +| Result | Test Name | +|--------|-----------| +| 👌 | cgb-acid2 | +| 👌 | dmg-acid2 | +## Acid Tests +| Result | Test Name | +|--------|-----------| +| 👌 | cgb-acid2 | +| 👌 | dmg-acid2 | +## Mealybug Fifo Tests +| Result | Test Name | +|--------|-----------| +| 👀 | m2_win_en_toggle | +| 👀 | m3_bgp_change | +| 👀 | m3_bgp_change_sprites | +| 👀 | m3_lcdc_bg_en_change | +| 👀 | m3_lcdc_bg_en_change2 | +| 👀 | m3_lcdc_bg_map_change | +| 👀 | m3_lcdc_bg_map_change2 | +| 👀 | m3_lcdc_obj_en_change | +| 👀 | m3_lcdc_obj_en_change_variant | +| 👀 | m3_lcdc_obj_size_change | +| 👀 | m3_lcdc_obj_size_change_scx | +| 👀 | m3_lcdc_tile_sel_change | +| 👀 | m3_lcdc_tile_sel_change2 | +| 👀 | m3_lcdc_tile_sel_win_change | +| 👀 | m3_lcdc_tile_sel_win_change2 | +| 👀 | m3_lcdc_win_en_change_multiple | +| 👀 | m3_lcdc_win_en_change_multiple_wx | +| 👀 | m3_lcdc_win_map_change | +| 👀 | m3_lcdc_win_map_change2 | +| 👀 | m3_obp0_change | +| 👀 | m3_scx_high_5_bits | +| 👀 | m3_scx_high_5_bits_change2 | +| 👌 | m3_scx_low_3_bits | +| 👀 | m3_scy_change | +| 👀 | m3_scy_change2 | +| 👀 | m3_window_timing | +| 👀 | m3_window_timing_wx_0 | +| 👀 | m3_wx_4_change | +| 👀 | m3_wx_4_change_sprites | +| 👀 | m3_wx_5_change | +| 👀 | m3_wx_6_change | +## Mooneye Tests +| Result | Test Name | +|--------|-----------| +| 👌 | acceptance/add_sp_e_timing | +| 👌 | acceptance/bits/mem_oam | +| 👌 | acceptance/bits/reg_f | +| 👀 | acceptance/bits/unused_hwio-GS | +| 👀 | acceptance/boot_div-S | +| 👀 | acceptance/boot_div2-S | +| 👀 | acceptance/boot_hwio-S | +| 👌 | acceptance/call_cc_timing | +| 👌 | acceptance/call_cc_timing2 | +| 👌 | acceptance/call_timing | +| 👌 | acceptance/call_timing2 | +| 👌 | acceptance/di_timing-GS | +| 👌 | acceptance/div_timing | +| 👌 | acceptance/ei_sequence | +| 👌 | acceptance/ei_timing | +| 👌 | acceptance/halt_ime0_ei | +| 👌 | acceptance/halt_ime0_nointr_timing | +| 👌 | acceptance/halt_ime1_timing | +| 👌 | acceptance/halt_ime1_timing2-GS | +| 👌 | acceptance/if_ie_registers | +| 👌 | acceptance/instr/daa | +| 👌 | acceptance/interrupts/ie_push | +| 👌 | acceptance/intr_timing | +| 👌 | acceptance/jp_cc_timing | +| 👌 | acceptance/jp_timing | +| 👌 | acceptance/ld_hl_sp_e_timing | +| 👌 | acceptance/oam_dma/basic | +| 👌 | acceptance/oam_dma/reg_read | +| 👀 | acceptance/oam_dma/sources-GS | +| 👌 | acceptance/oam_dma_restart | +| 👌 | acceptance/oam_dma_start | +| 👌 | acceptance/oam_dma_timing | +| 👌 | acceptance/pop_timing | +| 👀 | acceptance/ppu/hblank_ly_scx_timing-GS | +| 👌 | acceptance/ppu/intr_1_2_timing-GS | +| 👌 | acceptance/ppu/intr_2_0_timing | +| 👀 | acceptance/ppu/intr_2_mode0_timing | +| 👀 | acceptance/ppu/intr_2_mode0_timing_sprites | +| 👀 | acceptance/ppu/intr_2_mode3_timing | +| 👀 | acceptance/ppu/intr_2_oam_ok_timing | +| 👀 | acceptance/ppu/lcdon_timing-GS | +| 👀 | acceptance/ppu/lcdon_write_timing-GS | +| 👌 | acceptance/ppu/stat_irq_blocking | +| 👀 | acceptance/ppu/stat_lyc_onoff | +| 👀 | acceptance/ppu/vblank_stat_intr-GS | +| 👌 | acceptance/push_timing | +| 👌 | acceptance/rapid_di_ei | +| 👌 | acceptance/ret_cc_timing | +| 👌 | acceptance/ret_timing | +| 👌 | acceptance/reti_intr_timing | +| 👌 | acceptance/reti_timing | +| 👌 | acceptance/rst_timing | +| 👌 | acceptance/timer/div_write | +| 👌 | acceptance/timer/rapid_toggle | +| 👌 | acceptance/timer/tim00 | +| 👌 | acceptance/timer/tim00_div_trigger | +| 👌 | acceptance/timer/tim01 | +| 👌 | acceptance/timer/tim01_div_trigger | +| 👌 | acceptance/timer/tim10 | +| 👌 | acceptance/timer/tim10_div_trigger | +| 👌 | acceptance/timer/tim11 | +| 👌 | acceptance/timer/tim11_div_trigger | +| 👌 | acceptance/timer/tima_reload | +| 👌 | acceptance/timer/tima_write_reloading | +| 👌 | acceptance/timer/tma_write_reloading | +| 👌 | emulator-only/mbc1/bits_bank1 | +| 👌 | emulator-only/mbc1/bits_bank2 | +| 👌 | emulator-only/mbc1/bits_mode | +| 👌 | emulator-only/mbc1/bits_ramg | +| 👀 | emulator-only/mbc1/multicart_rom_8Mb | +| 👌 | emulator-only/mbc1/ram_256kb | +| 👌 | emulator-only/mbc1/ram_64kb | +| 👌 | emulator-only/mbc1/rom_16Mb | +| 👌 | emulator-only/mbc1/rom_1Mb | +| 👌 | emulator-only/mbc1/rom_2Mb | +| 👌 | emulator-only/mbc1/rom_4Mb | +| 👌 | emulator-only/mbc1/rom_512kb | +| 👌 | emulator-only/mbc1/rom_8Mb | +| 👌 | emulator-only/mbc2/bits_ramg | +| 👌 | emulator-only/mbc2/bits_romb | +| 👌 | emulator-only/mbc2/bits_unused | +| 👌 | emulator-only/mbc2/ram | +| 👌 | emulator-only/mbc2/rom_1Mb | +| 👌 | emulator-only/mbc2/rom_2Mb | +| 👌 | emulator-only/mbc2/rom_512kb | +| 👌 | emulator-only/mbc5/rom_16Mb | +| 👌 | emulator-only/mbc5/rom_1Mb | +| 👌 | emulator-only/mbc5/rom_2Mb | +| 👌 | emulator-only/mbc5/rom_32Mb | +| 👌 | emulator-only/mbc5/rom_4Mb | +| 👌 | emulator-only/mbc5/rom_512kb | +| 👌 | emulator-only/mbc5/rom_64Mb | +| 👌 | emulator-only/mbc5/rom_8Mb | +| 👌 | misc/bits/unused_hwio-C | +| 👀 | misc/boot_div-A | +| 👀 | misc/boot_div-cgb0 | +| 👌 | misc/boot_div-cgbABCDE | +| 👀 | misc/boot_hwio-C | +| 👀 | misc/boot_regs-A | +| 👌 | misc/boot_regs-cgb | +| 👀 | misc/ppu/vblank_stat_intr-C | diff --git a/spec/gb/test_results/readme.md b/spec/gb/test_results/readme.md new file mode 100644 index 0000000..15d50ef --- /dev/null +++ b/spec/gb/test_results/readme.md @@ -0,0 +1,149 @@ +# Test Results +## Acid Fifo Tests +| Result | Test Name | +|--------|-----------| +| 👌 | cgb-acid2 | +| 👌 | dmg-acid2 | +## Acid Tests +| Result | Test Name | +|--------|-----------| +| 👌 | cgb-acid2 | +| 👌 | dmg-acid2 | +## Mealybug Fifo Tests +| Result | Test Name | +|--------|-----------| +| 👀 | m2_win_en_toggle | +| 👀 | m3_bgp_change | +| 👀 | m3_bgp_change_sprites | +| 👀 | m3_lcdc_bg_en_change | +| 👀 | m3_lcdc_bg_en_change2 | +| 👀 | m3_lcdc_bg_map_change | +| 👀 | m3_lcdc_bg_map_change2 | +| 👀 | m3_lcdc_obj_en_change | +| 👀 | m3_lcdc_obj_en_change_variant | +| 👀 | m3_lcdc_obj_size_change | +| 👀 | m3_lcdc_obj_size_change_scx | +| 👀 | m3_lcdc_tile_sel_change | +| 👀 | m3_lcdc_tile_sel_change2 | +| 👀 | m3_lcdc_tile_sel_win_change | +| 👀 | m3_lcdc_tile_sel_win_change2 | +| 👀 | m3_lcdc_win_en_change_multiple | +| 👀 | m3_lcdc_win_en_change_multiple_wx | +| 👀 | m3_lcdc_win_map_change | +| 👀 | m3_lcdc_win_map_change2 | +| 👀 | m3_obp0_change | +| 👀 | m3_scx_high_5_bits | +| 👀 | m3_scx_high_5_bits_change2 | +| 👌 | m3_scx_low_3_bits | +| 👀 | m3_scy_change | +| 👀 | m3_scy_change2 | +| 👀 | m3_window_timing | +| 👀 | m3_window_timing_wx_0 | +| 👀 | m3_wx_4_change | +| 👀 | m3_wx_4_change_sprites | +| 👀 | m3_wx_5_change | +| 👀 | m3_wx_6_change | +## Mooneye Tests +| Result | Test Name | +|--------|-----------| +| 👌 | acceptance/add_sp_e_timing | +| 👌 | acceptance/bits/mem_oam | +| 👌 | acceptance/bits/reg_f | +| 👀 | acceptance/bits/unused_hwio-GS | +| 👀 | acceptance/boot_div-S | +| 👀 | acceptance/boot_div2-S | +| 👀 | acceptance/boot_hwio-S | +| 👌 | acceptance/call_cc_timing | +| 👌 | acceptance/call_cc_timing2 | +| 👌 | acceptance/call_timing | +| 👌 | acceptance/call_timing2 | +| 👌 | acceptance/di_timing-GS | +| 👌 | acceptance/div_timing | +| 👌 | acceptance/ei_sequence | +| 👌 | acceptance/ei_timing | +| 👌 | acceptance/halt_ime0_ei | +| 👌 | acceptance/halt_ime0_nointr_timing | +| 👌 | acceptance/halt_ime1_timing | +| 👌 | acceptance/halt_ime1_timing2-GS | +| 👌 | acceptance/if_ie_registers | +| 👌 | acceptance/instr/daa | +| 👌 | acceptance/interrupts/ie_push | +| 👌 | acceptance/intr_timing | +| 👌 | acceptance/jp_cc_timing | +| 👌 | acceptance/jp_timing | +| 👌 | acceptance/ld_hl_sp_e_timing | +| 👌 | acceptance/oam_dma/basic | +| 👌 | acceptance/oam_dma/reg_read | +| 👀 | acceptance/oam_dma/sources-GS | +| 👌 | acceptance/oam_dma_restart | +| 👌 | acceptance/oam_dma_start | +| 👌 | acceptance/oam_dma_timing | +| 👌 | acceptance/pop_timing | +| 👀 | acceptance/ppu/hblank_ly_scx_timing-GS | +| 👌 | acceptance/ppu/intr_1_2_timing-GS | +| 👌 | acceptance/ppu/intr_2_0_timing | +| 👀 | acceptance/ppu/intr_2_mode0_timing | +| 👀 | acceptance/ppu/intr_2_mode0_timing_sprites | +| 👀 | acceptance/ppu/intr_2_mode3_timing | +| 👀 | acceptance/ppu/intr_2_oam_ok_timing | +| 👀 | acceptance/ppu/lcdon_timing-GS | +| 👀 | acceptance/ppu/lcdon_write_timing-GS | +| 👌 | acceptance/ppu/stat_irq_blocking | +| 👀 | acceptance/ppu/stat_lyc_onoff | +| 👀 | acceptance/ppu/vblank_stat_intr-GS | +| 👌 | acceptance/push_timing | +| 👌 | acceptance/rapid_di_ei | +| 👌 | acceptance/ret_cc_timing | +| 👌 | acceptance/ret_timing | +| 👌 | acceptance/reti_intr_timing | +| 👌 | acceptance/reti_timing | +| 👌 | acceptance/rst_timing | +| 👌 | acceptance/timer/div_write | +| 👌 | acceptance/timer/rapid_toggle | +| 👌 | acceptance/timer/tim00 | +| 👌 | acceptance/timer/tim00_div_trigger | +| 👌 | acceptance/timer/tim01 | +| 👌 | acceptance/timer/tim01_div_trigger | +| 👌 | acceptance/timer/tim10 | +| 👌 | acceptance/timer/tim10_div_trigger | +| 👌 | acceptance/timer/tim11 | +| 👌 | acceptance/timer/tim11_div_trigger | +| 👌 | acceptance/timer/tima_reload | +| 👌 | acceptance/timer/tima_write_reloading | +| 👌 | acceptance/timer/tma_write_reloading | +| 👌 | emulator-only/mbc1/bits_bank1 | +| 👌 | emulator-only/mbc1/bits_bank2 | +| 👌 | emulator-only/mbc1/bits_mode | +| 👌 | emulator-only/mbc1/bits_ramg | +| 👀 | emulator-only/mbc1/multicart_rom_8Mb | +| 👌 | emulator-only/mbc1/ram_256kb | +| 👌 | emulator-only/mbc1/ram_64kb | +| 👌 | emulator-only/mbc1/rom_16Mb | +| 👌 | emulator-only/mbc1/rom_1Mb | +| 👌 | emulator-only/mbc1/rom_2Mb | +| 👌 | emulator-only/mbc1/rom_4Mb | +| 👌 | emulator-only/mbc1/rom_512kb | +| 👌 | emulator-only/mbc1/rom_8Mb | +| 👌 | emulator-only/mbc2/bits_ramg | +| 👌 | emulator-only/mbc2/bits_romb | +| 👌 | emulator-only/mbc2/bits_unused | +| 👌 | emulator-only/mbc2/ram | +| 👌 | emulator-only/mbc2/rom_1Mb | +| 👌 | emulator-only/mbc2/rom_2Mb | +| 👌 | emulator-only/mbc2/rom_512kb | +| 👌 | emulator-only/mbc5/rom_16Mb | +| 👌 | emulator-only/mbc5/rom_1Mb | +| 👌 | emulator-only/mbc5/rom_2Mb | +| 👌 | emulator-only/mbc5/rom_32Mb | +| 👌 | emulator-only/mbc5/rom_4Mb | +| 👌 | emulator-only/mbc5/rom_512kb | +| 👌 | emulator-only/mbc5/rom_64Mb | +| 👌 | emulator-only/mbc5/rom_8Mb | +| 👌 | misc/bits/unused_hwio-C | +| 👀 | misc/boot_div-A | +| 👀 | misc/boot_div-cgb0 | +| 👌 | misc/boot_div-cgbABCDE | +| 👀 | misc/boot_hwio-C | +| 👀 | misc/boot_regs-A | +| 👌 | misc/boot_regs-cgb | +| 👀 | misc/ppu/vblank_stat_intr-C | diff --git a/spec/gb/test_results/screenshots/cgb-acid2.png b/spec/gb/test_results/screenshots/cgb-acid2.png new file mode 100644 index 0000000000000000000000000000000000000000..e227dfe5a905ca5658edf6d61b7ae4dedb64fede GIT binary patch literal 1940 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 literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/dmg-acid2_fifo.png b/spec/gb/test_results/screenshots/dmg-acid2_fifo.png new file mode 100644 index 0000000000000000000000000000000000000000..ebdd4a9820195e2f370d204360db64d294f84faa GIT binary patch literal 2027 zcmbVNeN>az8Gqk*Nr^@w;FeIQBO=Vx*%+y_E?6L886VmLQi2dbf-<0~g4Rkj5Z)IY ztr@mZ+G!mhL1|A&_%H|*iW1`Y0hO3S4Aww!JBQGGc7~9Imyo@1`rmeT>i)PN=XZb4 zbMJHS?>>(>B`JRW+Kp=g09c=xz)r=+7Od5gUcq`p_>J2D;CC&N%}P7nXl*=JoRzo^ zcXA~Ajf-yszlh~+p*vV(zn66_jw@(U>qqkL)>v${hS??emEt%xEDy-ccN(9d=>}N7 zJY~Eouw=a$3?5dx8|;)qr?ItBR58EQW0!PpExTNRqRWFCb)kAVa`>q$-q`=lH+M#_ zC>iORNr$6HE%;HP?}uux)gbdUE_$rXl4hMW&rmNV?i$tLHxHG}doqc?6k(K&QSiL; zwaQ0hLtJ0r%*F*8qmP+fb8V2xnTGk7lNP78wWq51#;=JB^R_LE;AD*&z=&f7JKfVAUGy(}Y%0d7h5yAmk zBq}gj1%N6tjsSc^WdaI32w{}Ro1CzwCF#eHw(~yaGEKOW0cf@j|J6$ZhbN#6Z|=7+ zSB?g7gDZobkR0=I%EuyZCAA)6_8}88TTD?N?(hV$ja(P0_4?7yGtUb_EtD!9^tDt6 zo;yHq!M0Cxh4k)(B24%+VnUN?`7Ak(8aIUSLD{=g@Z(#ADk?SgHQqTE@hp`Y1TIvQ zUtu9(kcCKFR|O>ECMn!t99U0K(0g|hps+w*Zc!cymk_E_KE6dI)Ugp!cS+u!O7GG? zN($(DtFXAR)T=qu*yUHb9DjFrVL?MY79Al;h2WK z4OHlSo}r2l>2n&THA92$LDd4ICM&+t#eQ;Nacd8#?3pinD(R zuaZVL$@lKZ$rleAFZ{Fxw%(i44xR44?oJj(=D7IjGT#Mvvng)X;UJLfQMO3gJ5B0N zq2OJP&r+VGs{wRas1&e(cCR>Q!kOaZHW7~dqvUcq&_ia6Doin1lD!Kw_dGr`C)nv} zi|PLj&JBOxuX=|UCT2Q{av>-z5jU_O4I)t!LsdE7Uqpb!Akg-m5{FBuCmg@H5Kd** zvytn)bP%i$gY;jE))4B#0%vwu_cM{G&lwX5xXDep$?ccoJK4x177|te?Zn$FP{!U% z$yN-?yU8#3aL2K7)zdL9J`i~=ri2=cTKRfDj=9-8JhyzEzy5G5^$CK}3I-o1KfKApMvMLxG zC4&aI($oQ~l5-I|$Vi#o_%Uzkk-g9B1w1!3n$BH{!kV+hE9{kqz)86^+Ufma{c9D@5wyp71bcMV;d z>Mp57p}KBMG>l1qM)@E`S-x>J$=g}j$M|Hd zRgR`f2EE5hJq}sGk5`1P@)y(n^yFme$nw%thE-YYLVIG=x6+a1s^IlU=NG4L{yFE1amqIIEsev5z3p9ROVDJ8B@`v>hI+Od<1=+-11qpVBoD0FRUFvp+%)$$ki zs|{YE(<2>=5{eeg&5NmD2WVW6nz1sOdFo*^ook%Kg1GwR^HXCMRIv~AGdwRnlIK4Q r?TfaVWIlE!d^@`(G@JEP?SX-!6T8G!8;_B(A`T?}DT&<`%d7e~wFAn{ literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/expected/cgb-acid2.png b/spec/gb/test_results/screenshots/expected/cgb-acid2.png new file mode 100644 index 0000000000000000000000000000000000000000..92e9c09a0a019646505d1ba242262d6b862972fe GIT binary patch literal 870 zcmeAS@N?(olHy`uVBq!ia0vp^3xIe63p0?con5;bNU@|l`Z_W&Z0zU$lgJ9>DFdh=goTGy&?tFQlsN+f zleedfV@Srmw=-jl4k-v6fBE6T>WbzkEN3Q2t9)d+CLA)q_Vm}&pJI6gCzR#v&R%pt z&;FlI%gmk@mt%7rXHGCb_HCQ&DWAKm_JuR{zqxSI|NiNGjnjR#=S!?#FQ{pduDQV1 zs*`Yh2CJOaUG4_z8Lv`Iz*+{snqpJ>A%O_#x+mUmvIPp0SKbzTKiE(3so!_=3cahbx%+lx&ox7`EF! zJs@b~?EK&tx1(*rl>-YU4ZfcHWOd+KaNa(Rx7&R`_ulhb{NB zO|*(viA}w2wyx_68`tv>ikS?t(LF#?7~wbJX$R$7k7BP#=g3v>U1G6+IL cZhlH;S|x4`3lF$%18QLKboFyt=akR{045G-Y5)KL literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/expected/dmg-acid2.png b/spec/gb/test_results/screenshots/expected/dmg-acid2.png new file mode 100644 index 0000000000000000000000000000000000000000..345a6c496a8fef8501c4e1f508a64edb3076a703 GIT binary patch literal 787 zcmeAS@N?(olHy`uVBq!ia0vp^3xIe66Odqdkf;6^NU@|l`Z_W&Z0zU$lgJ9>DkKdXUUeECVola@QQY zmD2j>ug}!2=@9>u$Y%CY%&geJPUN$JRodhaFBjU{@6p~P;Wdr(@H-ps^6;4-)=X>> zQk=%h>f|bRN3moo>tp}f(;IL0pPT3N?Nf?=#Xqq$d9IU24>iInIN!XS@qN9_@9-JC zzrUAsRO}3&!9D$hY`J6s=l-437@7ag|IZx3<#RM?ft=K>&?Av63g4Byp7SvFF=xrT zEw+2!Exfk4=h?0=+gD8!IJ(yO$Z~D%CZ3NwOmmNZWcd1!Vf(bO;)*R_CludK&54eE zwW{|4x{Sp;qNQ!H@EI*jOgq4mobac1*RO;64!{5l*E!$tK_0oAjM#0U}T_cV4`bi y5n^a!WoT(-XsB&yWMyE`b7R$W6b-rgDVb@NxHbH36^aIGVDNPHb6Mw<&;$S;tw)jo literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/expected/m2_win_en_toggle.png b/spec/gb/test_results/screenshots/expected/m2_win_en_toggle.png new file mode 100644 index 0000000000000000000000000000000000000000..66da9487f7dc1d487051dc496693befc05ec7907 GIT binary patch literal 718 zcmeAS@N?(olHy`uVBq!ia0vp^3xIe66Odqdkf;6^NU@|l`Z_W&Z0zU$lgJ9>DA_0R^j$gs;x#Qa!wf(Dn)O}T?eJTfM?%g#_iEzJ@~b0G6~2rqhKFVA>pvVHQ+Qm@T3-(H*X^mFQGJGqaq0=>AOCTG{b%GqKYxhb7{d7pdEzDqNd zmumVx+nICNY{|XiJx^lxo-dkr`+)Ma|36L`KAZd8BH~|f_tSYV?#CLaK2mPC>D#uw zSn~M6^JnEQ9=~{gs?OAod;hZb>i1X8Kb-&h@!l(TyEtS%einD?ZE|0Bky|~aEh9w9 zs5k%VkD$fVlrP@s%fIq#>9HCg<+^`PD*`{9Fgdk#UDgyOrCzVKGmpJq(wFS}_}1yI z>oRPr>RhCj{+yVx#ch@8g%z$UQg4->MO>I}Ek zcZpIWXrVqR>y0q_1b+& zVCB9xDN$RG#Y@;TbEX!$NKGwlk@|n|PsHUFYczpIrk*#^b8=bsaUICwCnrp_9-lCg z`&ZLzqLt6x>GnJ9UWQPw=Z^CRy=x8Zje1uvXwTfkDLC`k?}>8t4DJPGvx`{0?g1lR zwZt`|BqgyV)hf9t6-Y4{85mmV8d&NYnS>Y`SeaT_nVM)D8d(__oV5Lisv$Q&B{QuO Ww}u@FZb?8J7(8A5T-G@yGywqI(J&YQ literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/expected/m3_bgp_change.png b/spec/gb/test_results/screenshots/expected/m3_bgp_change.png new file mode 100644 index 0000000000000000000000000000000000000000..1eaed2c826447bd6843564aaea66c23f1cb18cab GIT binary patch literal 404 zcmeAS@N?(olHy`uVBq!ia0vp^3xIe66Odqdkf;6^NU@|l`Z_W&Z0zU$lgJ9>DO>_%)r1c48n{Iv*t(u1=&kHeO=ifu}HH?^O~Jn`v@r1?&;zf5^?zLwS~L~40u=$ zsH-YQYVNpWv3g3%%n9o*sP1x^ma%w21*7-0{|ifvHmy1S;_ttZxQVOO`6oDfWH8J= zaWhm;;pNFYCLEl)35;RgR#BY&ryQijD2pC z0iO<^6>Fl*4A~N{Lly-VFGN}rAMmSp-D~?QZVvP;gKCLuL`h0wNvc(HQ7VvPFfuT- z&^0j8H8Kk^w6HQVw=y)-HZ-y_FnAGg(g;OEZhlH;S|t_@#t;psP2)6y8W=oX{an^L HB{Ts5sz-dT literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/expected/m3_bgp_change_sprites.png b/spec/gb/test_results/screenshots/expected/m3_bgp_change_sprites.png new file mode 100644 index 0000000000000000000000000000000000000000..8dd426981c59315a67d2fc57e5a9c69dc127fda1 GIT binary patch literal 794 zcmeAS@N?(olHy`uVBq!ia0vp^3xIe66Odqdkf;6^NU@|l`Z_W&Z0zU$lgJ9>DO>_%)r1c48n{Iv*t(u1=&kHeO=ifu}HH?^O~Jn`v|Cs*VDx@B;xSaX&bYc97SB) zZ_GUUr@e7QLVDNVt*p`ZP9DmeH;I)+x!ln6_$05rVu?uMr?ig6Y@3Q(F1|bX(C$2M zWnYlje7odQxgN_`-RDczOIYY%6>7X~%3|Jat&%iT`qj%{ACoG~pYiW-S+K@WP~#ep z$m<`S5zpGHZq=QcD!=U5mFo#z4v}*e0@j3cfTh^3Zw}_KlhL#nIre7#L6*i%yPOt8 zd>7ET_EC5v&${OgdJPS_EVr({@aimWa@g26zazE4wyo(+$L*_{Th_spsXKRmo3QA; z#_G*1QOmV6WbG%OzECx9-`_9)|L^}j_oYBUSbZqVmi=6|OkJgaTy&aWOUgLh7n9k> zp|LuBw^T!ttv3hM!t|6~bvZQ(dsj*|Y^o}H!5pwgU6k>tRh`nbgI{|#^V^%*O*nSi zjv=*lVkv{hHP>I9FgHoXGH@@Nt+d+IDm`iKMN8q=!0?ca;orp|=3;(yo!j#>QHzVy z);?AT3i{_MSXkVj^6cv2!*e3}wHd5lzvzr$yFNKx;bNor2id#x4YX%|Rk-T_5^lS0 zeD}ZKS$>td=T95F2+O~ocp>WfY*&dAX!!iRlJl9_^SM4yWOe-aQh}0d>r-Kp%irod zJO)bk@8WC9D}8Rk-*j%*X1?ZgAm1c@{lgGqE#sg4_mw*^t*Dl`MwFx^mZVxG7o`Fz z1|tJQ3ta;fT_dv)LklY-b1OqLZ9^j~1A`XGbMSu zyD%^tw-j{%dF&;gzOL+#*txhw4HYf@mjdOzJzX3_A`ZWuwz2E6fk4~QRfc{G!y|U2 zP2jd~iqn0NX1%}ZVDG~&8z-MTmEC)sPHFpI$`Q=VTe9Zu>P7XAr#E?v9Fs`@WBiNj zb?}*RTMy01z5Ax>gw^i2zVMLnzuP%Wa?*G2QI(3mEhrf5HpP4!r|N&t$5Xj9gC4rK zzsmIH-Kgq)I>`U_gcs7e+0m2C&wt&yZK}$?A1SwgoSI$yw|>z#eeWdQ&aSu<{R^J% z3KfxhF4_56?@WZZmwK|opLbHvW1HkDk2k$8k!ip8v7x}`X|N0KnJnhjC?6{H>nW<4Z+Nco zUa3{z^m`vvPklUL^VE9YBAKNhd%adlxGQ(2{LnqMF-G`(vCec!Wtp?PUc1QX7roqi z=ZFR0C*PCN``j$|otwW|;&^ZVl}QqhBi3|&w-h_wBiL7Uw^XT5_|!**-pa!=Pc6V| z&X}KC64+PfyXXAGX+XoSHa~8D-2d#yhvWJ0`}w}QxBpt~{P$w*Tzk2|dYeW6G6L## z0zt^+Ra$ vWMyP%Wn!sqXk=wz@Fe1EAc}_E{FKbJO57TzviiOSYGCkm^>bP0l+XkKeQsnI literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/expected/m3_lcdc_bg_en_change2.png b/spec/gb/test_results/screenshots/expected/m3_lcdc_bg_en_change2.png new file mode 100644 index 0000000000000000000000000000000000000000..d042a80c907292b9372fbdf1792c662fa0546061 GIT binary patch literal 778 zcmeAS@N?(olHy`uVBq!ia0vp^3xIe66El$fbh|?sNU@|l`Z_W&Z0zU$lgJ9>DOMd z)&C6_ct3Fiau}1m-CYPz6f=_H=O!i8%ar+QzKM z4gziU#pyAvOFAaKIuRTr`N%zBVX4EMz})cbFIMsV7V%UlSaM1BTksv_6&>php2)7d zFs*6!RWGmUGo5@B>NmTsakKwlv*y^bJ2Q9;W+tjeM(E5~visbwr{J>0c;y`ZDVFQrvAZ`uF1wx{Qt)Q?ya|sj)42NT z%b%Lp1QpmkD`}tE_ITTu)~?SMR$MaS&Tg#7_r2z{RBJf%@loe)CX>$>^PLUlzn|&i zcI`{GvpV&@^u&q>H_B~QkE@;&-EVw;-H+z=O2_rDTJ;!zw0S2#S6{?((n>b>MrG@^ z9;v>CuQe@~{a7=je}C*p{^FxGDHU~dI%T#`T`Z#hse12Cp1wGbMSu zyD%``?Gj!BSFh;vH5z+1q*)6lEJ;$IA<5SiZFI3ySLgS2*@pEQpSY8z&foLJ_F{2?NO;4WKYAS7MF?^`t9rfOm@u8E1(6dcX z9@(x`(&5ZLDWYX^+M$UB>lvkWy^;j|b_qr8`kcYw?>6O# z(wr|BzU+Az9QK^^!=z4-!h4(#y1VjNAM6!X-N>O{t9+oJwdPn$vqq<@#YxsFF)z+d zFc1gH?CjdG#2RQ5(2RL3`==J!KI}7?3X(Y6qO4fceabWnC^O>=v+bveyOUb$n}I>4 zTH+c}l9E`GYL#4+3Zxi}3=A!F4NP?njY13!txPR|NZZiJ%D}+*WWEN9hTQy=%(P0} V8l0wFp90jt;OXk;vd$@?2>=(OtegM< literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/expected/m3_lcdc_bg_map_change2.png b/spec/gb/test_results/screenshots/expected/m3_lcdc_bg_map_change2.png new file mode 100644 index 0000000000000000000000000000000000000000..c84b45b92bd9ff2aa103c5f20512c4c60ad5a472 GIT binary patch literal 654 zcmeAS@N?(olHy`uVBq!ia0vp^3xIe66El$fbh|?sNU@|l`Z_W&Z0zU$lgJ9>DOMd z)&C6_ct3Fiau}1m-CYJzX3_A`ZWux-sjp zfk0b6XC;$*@Ja2|3ziBT^WMhZ?W^wECZI9%j*xwCyM?nteqwLiyeVogr`)-+Yr**w z9FhMlYdy?-1bovN0#2OS$=MM3X7-Ik4%bBXePFQp5b(vNdmg(IQkJZZDP z8}PM!T=cW<>O}Q~q+Ly``9AfpRiAQXUiBpN{e5F8kbvKflh(ICOf_ z4KJljolcx??_?+Wlw6nM>{7gT!|RlS@KQI)?wLk{`K#OB?D?cxr(D}1b|!^0>x$fS z(bqP5IxlV3>Atr4G_gePH~Y^< zPjEc=B(}fhtLn!_cTbz^D?Ukt|Gh2u{9Ewz!oWqBl0#qH6h&&;9?So6!d6at`R5yv zOWo7wo_m}yRqo_2`%g1GRD?QRlqLeH|5Nlc*GbMSu zyD%``?Gj!BN<#N+teenZ{{0}3XyCr5w>SZHk$v6lY%1 z*m11xh_9u}^aM3oNPC$+idIrv7MM)mU9kB`sL^WNxhsr zAeomV2PC=7^qHS{%G_g<@cH#H`Uk^yN7+dwFAqKjI#RX7HKHUXu_V>=o%Y@7#dm`npl|_Ya1F_85mstKX(s`hTQy=%(P0}8Wt^51*&IY@O1TaS?83{ F1OPH2bRz%& literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/expected/m3_lcdc_obj_en_change_variant.png b/spec/gb/test_results/screenshots/expected/m3_lcdc_obj_en_change_variant.png new file mode 100644 index 0000000000000000000000000000000000000000..8f35f76aa3b550ec8cd7f79b5cd20532ead4d166 GIT binary patch literal 452 zcmeAS@N?(olHy`uVBq!ia0vp^3xIe6BamQNelAuQNU@|l`Z_W&Z0zU$lgJ9>D$~U3| ztZZE`DJUtJy?ozaGO0A^(WlFs?`iw3sz^K)})Jb-iWOG zK9iFVe(9cR+M@c!+ea7uc5iOJbTCV>2du!Ser8yW=Pa25PC&UNa@;^|mlQbnH~s5iufZ;H&5#~}Gt zmOCvL)J~G^W;XMk#n<$i?<0TJ{vGq}yT51v{iRys8c~vxSdwa$T$Bo=7>o=IEp!bm zfhfetz{<$f%D`OP(8$Wb;Oz6}KokwR`6-!cmFOA_t&B|}8rC@W#sW1kc)I$ztaD0e F0swdznhyW~ literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/expected/m3_lcdc_obj_size_change.png b/spec/gb/test_results/screenshots/expected/m3_lcdc_obj_size_change.png new file mode 100644 index 0000000000000000000000000000000000000000..2692919b8579c7cbcb54093a43175a06716cddd8 GIT binary patch literal 737 zcmeAS@N?(olHy`uVBq!ia0vp^3xIe66Odqdkf;6^NU@|l`Z_W&Z0zU$lgJ9>GbMSu zyD%^tw-j{%dF&;gzOL+#*yWhGbmDmTOajV_db&7#_4!!YZygM^#?qGPTHQmb|g zuDhh8Sv$%0)FwSwDOts1Sv`IWZ`GcT6%Bz9CPKi>qn&;~!TTdb+Q0Gb~G15T3vC`1}ixpM}Sr@UJ=9Wpg9t<#c9M zj(yg4?CrW=Dx?lN_RTwR`Bl*avpJ7Ge~@nY`@@FOK(A=m<^;Po`4pwkJC@2BONb{a z%(6ImMwyY_tnib^f$OTDBo6JAO|-0HI$z6uaPHh4J{NBC@?>4kX58V^&0ruO7dYpH z&9*Da-nKyEH?K|GhcLx{Tp5SmRTbXm+dR8oHKDElpOrlu`>R4n+iO>n zoo&soBp2KEM@vi)ba@!;AZ=%yp?>K@Apc=^lbSC3)gIps6iS(ygZd2=wl*=&blCFs zRh8Uy1BK(u5+JHy9CEjonD8p`M@O6g#|+VzVr)ywfLayXy}ZPuooy$y1YmGl%sWp*`$@al_Ac4xWNaK%2} z?#CZS?gy`5aDDtI4NNntC9V-ADTyViR>?)FK#IZ0z|cb1z+BhJGQ`lt%Fw{d#6;WB m$jZRrxA^CL6b-rgDVb@NxHWWnd0z%GbMSu zyD%^tw-j{%dF&;gzOL+#*yWgb_*WTZr2&O*db&7zuQVZ; z{1DN3W_10M&AEkDdsfN`hoprZd>hD+{lM+rhch!4-b>E=Vr<}Iuz#=T-x}LEnLCd8 zXTN(KklHcP+QvBThum(r`^rpE#Sg3BNY(aptZ9_qdDuAlnEZYDR>|+b7jN2Xt8_um zFr!~&i#x|HGbMSu zyD%^tw-j{%dF&;gzOL+#Sb4;aBwJ-NmohLg>Up|2hD02GJN+Ohlc9hsdyqp^M4eZzZ@XtM#nR7p&}f@vp+wAiAAv2L2PaCi^forcHKHUXu_V>>KYn_7#doc8dw>bY8x6^85nr; ixP#&Xp&>UvB{QuOw+5@g|2u&i7(8A5T-G@yGywp-XToCu literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/expected/m3_lcdc_tile_sel_change2.png b/spec/gb/test_results/screenshots/expected/m3_lcdc_tile_sel_change2.png new file mode 100644 index 0000000000000000000000000000000000000000..a8093218a0b2cf6521cf2aa90ee0bc5a0628aab9 GIT binary patch literal 921 zcmeAS@N?(olHy`uVBq!ia0vp^3xIe63p0?con5;bNU@|l`Z_W&Z0zU$lgJ9>DOMd z)&C6{l8@!gy?GJHXH4>TcVXyYmGuB}*h@TpUD+RVNpc#?T#-9?f`Ngl-P6S}B;(%O z>9s|R4S0^8J-{gNjH!Uh<`m99mHTf@y6 zpR~C=QqLT}z5l=7jq-o_4r|0CUBVkXHkV%8%NMh)Hrbm>(jZ`wJYNStdo zx1!*G*Wss!5?MDn|N9xsJDanJw>Rb3_1J$UQJ0?7FSuv9#O(aCbiS3^Ja48u8>b|! zmk9&nw1oXKLGtOJ?^*iXvwCK5_l#`u46!49mMjVFvc(*y*%OXX#^G^IT@u zs{)f`%MF(#x(`=Jgfr$xeCthfox}7*Mr^@isXCv}zD##2UFWn{u5joIU=8}c)Zu~Y z?Og@=GPjsqUVq5ryEtXN%<=ALM_2pXJe-ot5EUZ_RRGwcg{4B{%yLMHyFf8T5aY zk!Mm^5}m?zP&sLj!GvQr%i5xER(Y%zXJ)SDl!&c-ByMp%V2^#ug86?!8dJ9S>^yzx zZz5aUwT1eRFYapgWAWr&FZ1h~V(p*T2mZv=t$65C?!g$u`%(S;)8l%VL%0iAyZ@Kl z$^@OzsJgfCG+(p)g`=#E#V<50CkY+9&gCD^G)<3vdgGThg3ODZ-{r6d9nX~EKAye! z&cm44TE;qyJ-!)xSG3KM*1BEt_JWl5hq*i3d2@V?gN3Z!wF`4iMb@?Z@0yck;XV6KytC;w&Gr)G=L%(~665lGw#!U= z9=Bb_j`^QOJMRX6zSEO`))ub_S{b4>)oUp#^~GbMSu zyD%^tw-j{%dF&;gzOL+#Sb15wL~XP?`4|`&#XVgdLn02py>8gm>>$DVU}C~t(Jw;L zte4oX9eP>rpg%MC2}4sj=MAoP?mxO3Pc~hX@BeS>V$ibTvanypA>n_@KWgTEXqnHn z`efW)1FbdIEuM-0T=WiqW@(*#FT%BD?V&%NF>TLZnr^%-{kz4$?5NP*o^zk~`3WCP zb*-pY*mM4<&`zH7n_~smNgw)oOEE??&Hss!&H0aO4;+cj-{aE4-+EIRq+q|vNB8_1 z)fVqyt%;|9*u0l|*p&J$y8DBjh&M=mm|~23OYvPrAFj>nMUwNL|6odZw8T72Fmbg( zwbJy5mEYoot@m#9=SYTV-@o;blbnHCOT6x(G>vH|QY+?dbeiGT;{3PQK=aJJ6RA1# z?3=43i#{qu%>ss+YKdz^NlIc#s#S7PDv)9@GBC8zH89mRGzu{^w=y)aGBVUQG_o=< jSYLnM6GcOAeoAIqC2kG(l*+#XH86O(`njxgN@xNAXpOnt literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/expected/m3_lcdc_tile_sel_win_change2.png b/spec/gb/test_results/screenshots/expected/m3_lcdc_tile_sel_win_change2.png new file mode 100644 index 0000000000000000000000000000000000000000..efd7a093c1b8603bff32a42dcf49c29f97483272 GIT binary patch literal 860 zcmeAS@N?(olHy`uVBq!ia0vp^3xIe63p0?con5;bNU@|l`Z_W&Z0zU$lgJ9>DOMd z)&C6{l8@!gy?GJHXH4>TcVXyYmGuB}*h@TpUD+RVNpc!nypUCuWME+O@^o%h&PCEUQfND4ond)meVYfyx>46rOyTVHf-3 z**|l>cJ6le39_HQ@0AJ@ej;UcvSPAh)hSpI(Q;j^=o__nu48n)kVS~0_!p$te!s0EyTXz+Q8Xz`A(ALbUn(381$ zD$)2%@cNmD)1HY27TdNcGK*e647BsZ)UO|64p&>2yr1WM;okJjuSt_${hZJ^>$&sO zTFC?7p6<@tHX+de*R`#?oh34Rjy`mka`)u?oXvXIP)ysBqyf4r+h zX5Q`|@2jczmjyDc32FE(A^+Hw>CU`~dP_Sxejanl`fh0X{nKYfM&VjPgWX+6^b6K4 z{84j)FIjvyvq#zK8M}2@p9$yiDzYCB zm7>@6!^P7xSFu_&9sghNuh>0jZ$o1FjHG5oeU3SY9ZpVUNassh5-Gf)WadLjkLq5D z8PlbWzA}_m&fz|uy;vaP+urO4=?Att%U^AfIaDCMGbMSu zyD%^tw-j{%dF&;gzOL+#*qJ!E_~w{SXa@?-^K@|xi8%cBf+62w1)kQ2%Qu9~S#m0* zblsHIMp2fm#>-FgMzK$2lC>`Vk=x0r`t9-LrbEgpvZwB?N`I{^8m$v`=kYqzwJ-Bj zuLW({IK6xRr)uqGtEd0JX!mEGS>bBo+Q-#jZ3?TutrD(2R(a>`@}B3btB?J>ntG<< z-TC=F$*Xp@DEC=B>cB=kj&5cuNLug_XNKrb<?NMQuI!IEh1tZc3xBQ30BYj(ba4!+xb^n7Z{J}Bp0>p1s-s0G z)=!@kx-`(#^XJC1s|6!VCmvM}=8)KZH9CbQ7 zVgEGASsSEcB6#&6a;p!vhA-dc7Rzp*d}D<}&xa~|4#9gl5t4^?%bGe>6dAMsSi&5i z-obD1t@6v>Ykn-^jfdO=ekh9lv)A~eB3LKZ&38vhCYs%qJx`#o&eMf`8Tax1M;9?q zZZ+O5d9iWjp`Cvhyg1Qh@@8=l)mg$7nGGy__8lk zNcM%^m;I}SWnbv@?0738`$DT{r@ol%3yq##h2pX=)OuD&3dp`t=~?p&NGbKKoh>N) zLZN4!jgagPou2h~ft1FX6?MY0J5Sr^0TEdX-7~8bMP)5C&#Yk*leJJi zvvx6%QarQHMO@ZG=gfMbBP=xTT;x@>D)88`e7;M`gNA}|_r4bg@*eJ+r*^S%?xDG{ ziHn$bw^aXr<-%UZQC_ar#rH?+&i&h(Rv)@2T#(ha`Ve;F1<;`t%1cTz;<@S>EH}ni UpPRt!4@@fzp00i_>zopr06fS6djJ3c literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/expected/m3_lcdc_win_map_change.png b/spec/gb/test_results/screenshots/expected/m3_lcdc_win_map_change.png new file mode 100644 index 0000000000000000000000000000000000000000..4e81b8a79bdcffca1d61b5a8dbc9e0ae0fea5040 GIT binary patch literal 456 zcmeAS@N?(olHy`uVBq!ia0vp^3xIe6BamQNelAuQNU@|l`Z_W&Z0zU$lgJ9>GbMSu zyD%``?Gj!BUftDnm{ Hr-UW|(srPJ literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/expected/m3_lcdc_win_map_change2.png b/spec/gb/test_results/screenshots/expected/m3_lcdc_win_map_change2.png new file mode 100644 index 0000000000000000000000000000000000000000..dacf86b86e7073592c67d797ede0e3d6ec1edc32 GIT binary patch literal 561 zcmeAS@N?(olHy`uVBq!ia0vp^3xIe66El$fbh|?sNU@|l`Z_W&Z0zU$lgJ9>DOMd z)&C6_ct3Fiau}1m-CY}jNf-A&HUM|?lz-dA(X>d z{;*4G)bq%BZ#UKKo5EMNPuy}dL>qJ=YKOW`Anq1TTc%P^3hQ4*4erpzU%N4H?R(@cy zL~h!fh!){z9=VN{K^6_aCHNVhM6?`!B)fKJL*LS4GPPHM)^NUi{J2g3P9evG+C7WD z8~$rIba#6HIOD+YA9G&)mr)h!bWxhz zm0Xkxq!^403@vpH4Rno6LW~ToOiio|OtcM+tqcr48%(%}q9HdwB{QuOO@oQ0m5~ud U!}J$^Q9un0p00i_>zopr08sPRw*UYD literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/expected/m3_obp0_change.png b/spec/gb/test_results/screenshots/expected/m3_obp0_change.png new file mode 100644 index 0000000000000000000000000000000000000000..065e2fefeeea8f76f2698a6fc4dbf070eaadf29a GIT binary patch literal 599 zcmeAS@N?(olHy`uVBq!ia0vp^3xIe66Odqdkf;6^NU@|l`Z_W&Z0zU$lgJ9>DO>_%)r1c48n{Iv*t(u1=&kHeO=ifu}HH?^O~Jn`-p*o(aY1tF(l&f)oYGjha5y) zFY@ahSj*wZ`h7LCbd^Sgp2G<{7b{*#t!ehw7R_2slJRl>swa3adL+ynOxrKjnw@J^od#Q_^$^&S^I z*bU`gKeEXD-p9qAf~D6KFP?s3^FMoUq0@)-_6P5CH@fH@{=6rQ!!Dk~?pigAI_qrp zBFTB~^%H~6Rz1#gC|I8G8KmOMYBjOFAG73r_q4wa>Wn$|+>zPEb-9b{Tvyk*Up<}} z+3-s#Xj!Rhz0z^K9N7=jv4GL&;+#q5v(5>bz*QXFdoRPv`$EYz<%y?%*rYYEu zPKv_Lic@XmfB~&q;u=wsl30>zm0Xkxq!^403@vmGOmvORLJTdejLfYJ&9n`TtPBiZ k1e`QN(U6;;l9^VCMT0R!!)envO`rw_Pgg&ebxsLQ028*?5dZ)H literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/expected/m3_scx_high_5_bits.png b/spec/gb/test_results/screenshots/expected/m3_scx_high_5_bits.png new file mode 100644 index 0000000000000000000000000000000000000000..d7e25b5686c39e480e2f694d70c8c73aef5184d1 GIT binary patch literal 1014 zcmeAS@N?(olHy`uVBq!ia0vp^3xIe66Odqdkf;6^NU@|l`Z_W&Z0zU$lgJ9>DD{E3y;fKo`kE_+hIvblVSyx6ZS^-cJD5omQwAS`k|12`uQ8nxe7a) zADjyR{^}94u3hkH3-y&(!^J;|PZgW}rA^G*bGn`C+{_PE{7qd?yGr^uZA;#FobR|> z$PwE|5qZL*fm^P>4ymf+-TL73EK#W&Yx1W}khveA(-$LHySC3oQ2*vctwl+tDqWNQ ztLJNMt1Wxd_42c#`??6(m5Z!`j=nk}dhzuggV30^M;@Zj-3ym=xj!z` zUAb7-kTs8IX`CvSzP?ULYuf7>NxjEcCIZ!bAJl`t?PwPVM^~Xcxm5G>HQ2rK5Y4tJa_x0rwZlYJ zZsnt*%ccB*F5RVfokgbaUA!D<%}S-LRQFXKH+3wFT+dz)3B3L=X5*Ru7{R?^?(u@6 z{m-+*3u8m-^$2fomS`TG~|_Q+zb_#!;ZfKSSIXbr@_0hEJ}n zpXm7`i`U=C`S?QXSl#zM$Ah<&S_Jp(;!E#Ry1naos7v>@cOOOh-*eAgv{rs*Qg)xq zMVXmOP0MRkgH7C*W&F-wR*_ckE21mcx#*~J@SV5ZwXW59$3#+RE7!PoyRZ78e!N~Q z@4RNs(Z9tp;ev_F{Prbn`O6bHMK}I;*X|l$k^A%I>lrGaDhho4t8p8cIaEtrBT7;d zOH!?pi&B9UgOP!ug|306u8~oQp}Cc@g_Vi9wxN-gfx#yYMFVdQ&MBb@0534G3IG5A literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/expected/m3_scx_high_5_bits_change2.png b/spec/gb/test_results/screenshots/expected/m3_scx_high_5_bits_change2.png new file mode 100644 index 0000000000000000000000000000000000000000..2e64918252fbc4e3694f037f2981fa5e26184e87 GIT binary patch literal 647 zcmeAS@N?(olHy`uVBq!ia0vp^3xIe66El$fbh|?sNU@|l`Z_W&Z0zU$lgJ9>DOMd z)&C6_ct3Fiau}1m-CY|ia_ZKPZ!6Kh{JEE?$2vB z5NO})Qpq%Fn0W{ch1sFj*(%LF%PhyE&^;#l*j&wA3#(`h$9cq+x!rhGd;X~k)`z5DtFx9WQw zQMhpVeDbUl^`;=wHTSyDWdf@kO)tA1{7t&@bLyr6aRLPWOf3 z-?N4L$n%69M*u3VMoc`9sHrvF$yjXMChEME^ z$2HrN$tpsfE=m(UfYhJhI))7~GXHcorF{oRi)x8$L`h0wNvc(HQ7VvPFfuT-)HO8F zH8Ke?GO#i=u`)2xHZ-;}F!*dR;UbEL-29Zxv`RD$CYDyFmJki|wS;#9H86O(`njxg HN@xNA#kdB8 literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/expected/m3_scx_low_3_bits.png b/spec/gb/test_results/screenshots/expected/m3_scx_low_3_bits.png new file mode 100644 index 0000000000000000000000000000000000000000..bd7db2fd9a67b531a634fd5a589c4adcbfc9505a GIT binary patch literal 328 zcmeAS@N?(olHy`uVBq!ia0vp^3xIe6BamQNelAuQNU@|l`Z_W&Z0zU$lgJ9>DyN${xMnb&v)7G2l1IXO*chW9?236Q}2bzKVM>a!VlV&z literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/expected/m3_scy_change.png b/spec/gb/test_results/screenshots/expected/m3_scy_change.png new file mode 100644 index 0000000000000000000000000000000000000000..2468d226361a890fbe281875ad67fd56ef440054 GIT binary patch literal 2141 zcmZ`)dpy(YAOB|4avXYzno}+Zu_G}fZEff%Yc%FK90_y3B$8vwKor-Ct zbP+OLY=-!W@shD}DOMITHKyjWa{Ybz@At>=kLUS3&*$@be_o&G{rNo4bN)9HLFb3< zKL7xrJ*bQOn6}Uy`Dnl;PQD#JWsU$P>%`j(j4B zG=~3pdPiQ#QCnXLlp?I>mS@sxT4Uq=-pUt({&-r2$s+eXD+}){m$;n3uMTY8apB+D zFl%~i`NdMjot}K{QyK3&y{@LZ?=CB?z}G3`t*j%Q7R!lgAq$OO)@p-%f7WRoZk9k&c??HH7Zljiw zZ$(FW5RA zy0BABS+9d8+&MB`j_es@_q(sh;Pz`C^Vf^iYp7;pwzmwLE`!!K-o=hE^`&4^jCwzp zII=CpMh=4dgBccSn+YE=YUV-Z>%jp3+9sOPL?mc1fvpG4KAO)DV^Bt*>Ag|l8AMH5 zl-_$dtoWsopKSI|bd1Yk8C9`MQ#YR+wu4;0J(bQOQg%4;(f3Ux!R@qMp@^p3Qu~CR ze)^yigyL*xsOw>sSQ}J;7Ih;*CAff&og#cC+&IB)LwtF+C;k?^pG9k-Sll}FS(^6tSjT34HuDkE_BP)nrmRMYaO zD|QMsqkJ<_Hc_t4*>`B*Aa`QCQ$BTEHAUf33B_jBhvO_19!<>~vKrcsaoQ&9prl29 zM$c=H(`UMx$o)y_j2-@6%eJ)Wt$xn5_N*vm$|NoDoqmAs`~hXR>0#aw^Ge~MEYw@$ z9M}!U#&LJfO=8Bf=^Rds#ECzz6<(}kaIJf&seL(UC_2L>W(BnR-FASzJ+Q~3+ z%PRruZIt=yw;OA&FMY$%=$z?}DU7*z3R9u1xv@H{Y4d(~qZm(c0!teuRoL0L5_X!` zz7N2KN%)mIOkslsiCW9P4U3dwY{`<(T~vnBl4gIsK-ij<*mnZc?2^t|3{FB*TF$=q5MEv%~buFxy=jya!bJoyCv zMPux*r z7O!HlA~qoZ%?Au}{&5n7lVX-ecF2^}xl$Sygd?0sRC2$deiKKQJ*dc=uoKjK7$xT* zXALEcluSMF3S~Fo|6?M@U)U~nKB?LD5&vvwGFR>6xFveVeESYTcYADGNe4^HN(n=4 zi=8nSW>atLDmp#(jNxUoQmjZjD9H|~f#6SlTZ<>DE)gEQel($d48O8sjxOdo70^PV zBr3Q65Q4lH`FDPAL}RtyQ+Tv0GAFf)c_zwT600&7x(cS4E6BAlf>Apz+}2Z?9 zDxByRg+7a}={)n4u37>lqG2V@QJ z;*g1c%^Z8iJZFBS!}K09LSZa#y+!=CTXX{}X1y_#dNC)D{@JyE|5)qFdBTToM1D&l z4-O``*KM&S_@~^Dogkm1Tkrb2axcqPP;32xx>kTIgvZF9iO!yA7_HA6)uC7c5M%jT zGmd24&G&$3e!Vor9j%#LxdzAOd{3D;)b1i?HGLAOJy#&)TtrQlE%5^Q2V2tdf%NDI zdJHxyIR+GfgFOmkYroId!NDJe!lL$I?f2NA9I*EGW&2+b{0|^8DLOtj^Zx^SBNGxp O0N{xyxmO(s&;2i^ixJoW literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/expected/m3_scy_change2.png b/spec/gb/test_results/screenshots/expected/m3_scy_change2.png new file mode 100644 index 0000000000000000000000000000000000000000..ab60b582ea78d49416962c2644fbe989df23513f GIT binary patch literal 760 zcmeAS@N?(olHy`uVBq!ia0vp^3xIe63p0?con5;bNU@|l`Z_W&Z0zU$lgJ9>DTcVXyYmGuB}*h@TpUD+RVNpc#A9KKhv8z}wE)5S3)x?db z*>~%Ry~tm?yrl_h-)3@0S~PK-OI*3v!7}F#_Y~*E@{Y$FPEI^5EW_7+@}VT(-&D7v z3|7I@inE;++GE7qiXy7x|Eade2fZ^BWZ)|mn%?vzU{|P1w1aF#d%+6RXyGm2cG%`k zsH%AIA;k929A}pH_p5mxd?>88%azkVu`o>k&AuE)){_gxq)#_Ip78U;L)Pz4Ul&{C zhW=J*&xz)m&@1cRrA9nO2Fe!NAJM0HVS6&FV^^1_n=8 KKbLh*2~7aX%sIjU literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/expected/m3_window_timing.png b/spec/gb/test_results/screenshots/expected/m3_window_timing.png new file mode 100644 index 0000000000000000000000000000000000000000..30c9fa701add6bf026e10ae2bf0336653518909a GIT binary patch literal 300 zcmeAS@N?(olHy`uVBq!ia0vp^3xIe6BamQNelAuQNU@|l`Z_W&Z0zU$lgJ9>D6MJjmz-nDN=@AZ)UR6N8c~vxSdwa$T$Bo=7>o=IEp!bm zbqx(d3=OS}&8!SfwGEA|3=G^I&p$)akei>9nO2Fe!PLsc7^2~u^mlfk1_n=8KbLh* G2~7aa09HBx literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/expected/m3_window_timing_wx_0.png b/spec/gb/test_results/screenshots/expected/m3_window_timing_wx_0.png new file mode 100644 index 0000000000000000000000000000000000000000..de5d3f0c153108ea6c47182b3adf55c565b7e41c GIT binary patch literal 307 zcmeAS@N?(olHy`uVBq!ia0vp^3xIe6BamQNelAuQNU@|l`Z_W&Z0zU$lgJ9>DcA zWL<2T?*IAX9ut0FA=l-8bFE5WU0E3nWGrU0+Pg&m?txfWpfRc?t`Q|Ei6yC4$wjF^ ziowXh&_dV1QrFNj#L(Qzz`)AbRNK%9$aurXe-A}NZhlH;S|y4GGb>Y5D?GbMSu zyD%^tw-j{%dF&;gzOL+#SUEW*c~4xh`^mt-sP5_F7!q;#?R3MgBL+OKu9tM?H|{-T z6zY+>jqgWYaaV)%l zMXpNO)$#b1iSYc``{BK5*`3ZVE>2QO2g`Lh8}ykk%*+13bI92=us!;7S7$4~%?G1X zj&>`KF&z;)-Ls{|cFNWtMef0Ux@|jJlsNN`F7rJap!V+FjN8Fx^_)?PA9gLif8&TX zo0*E(HtS!$rqefUt(+J9`AQKqSBZSvcf7hvgmnXybl;|E}K0 znSHuFK&3M!k9%9(`oFc%r0>{83#sG(4}TOt$HRDbzRJ|Qz)(~zag8WRNi0dVN-jzT zQVd20h8DU8rn&|eAx4H)My6JV#@dEPRt5%XrvKNYXvob^$xN%ntwB7)T^Oi=!PC{x JWt~$(697DJ%FX}) literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/expected/m3_wx_4_change_sprites.png b/spec/gb/test_results/screenshots/expected/m3_wx_4_change_sprites.png new file mode 100644 index 0000000000000000000000000000000000000000..9620fe9e0e2cdbc78058fe099e9abca9ac0ba9ea GIT binary patch literal 296 zcmeAS@N?(olHy`uVBq!ia0vp^3xIe66Odqdkf;6^NU@|l`Z_W&Z0zU$lgJ9>GbMSu zyD%^tw-j{%dF&;gzOL+#SUEYRB>EIOF9L;PJY5_^A`ZX3xRLXK0mBi8_x4`XE^0_B zMk=&2n?)>Gzh2-^g_?O&-HD&Q*N->-fA}li%#o#+uYy}jR={yFvr2OC7#SE^=o*;n z8d!uF8Cn^cSs9sV8yZ;|7#!28J&2+qH$NpatrE9}?d3;nff^V*UHx3vIVCg!01eq# Avj6}9 literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/expected/m3_wx_5_change.png b/spec/gb/test_results/screenshots/expected/m3_wx_5_change.png new file mode 100644 index 0000000000000000000000000000000000000000..1d0d0582c89b3da124d3b4d17a371c83135c8d8e GIT binary patch literal 342 zcmeAS@N?(olHy`uVBq!ia0vp^3xIe66Odqdkf;6^NU@|l`Z_W&Z0zU$lgJ9>GbMSu zyD%^tw-j{%dF&;gzOL+#SUEW*O&?rpO9u*ddAc};L>zv5buVXwfq=t9X7P<8=>--W zH=Phz85<;S@NDi4j`wlWK{Zd*lN!!W6&04*r#!DT`D5$vV~@D`BaSdxKU9(Nmi4qM zJFh(Hir}4Wg_X)x^1CNl9jagU;zwt)dyxB^9~vu@4}EkDIzA;iIOzD2WapscTOO}n z(x)%;zM|wxpu248m5@i#0eZeFnNpwZt`|BqgyV)hf9t6-Y4{85mmV8kp)D zScDiES{a#InHp;w8d(__JgWZ~ilQMmKP5A*61RrQN}ex)8W=oX{an^LB{Ts5$E9^g literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/expected/m3_wx_6_change.png b/spec/gb/test_results/screenshots/expected/m3_wx_6_change.png new file mode 100644 index 0000000000000000000000000000000000000000..daaca82ad836f3c1af891f5b436683db978b82be GIT binary patch literal 577 zcmeAS@N?(olHy`uVBq!ia0vp^3xIe66Odqdkf;6^NU@|l`Z_W&Z0zU$lgJ9>GbMSu zyD%^tw-j{%dF&;gzOL+#SlPG~HFsv7xWmA}SnBEG7!q;#?G10fCIf+nhn;sN{1PJs z!)zC8_dD)k2>;A>A~?=?ZvE2E%=EMKzjQkUsvKA;Z5DL!>uII9t5bVL#La^2-eoMQ zx_7}N)+A#|*C(go?XppKHoji4nB_%;xlhrLuMWrCJo#MJZi-J#*`NL9gi5>8B$@Y; zN|tw9F9>@2xhh$TH9pyq{l-IOxzdC=^KJJ%`DD`E=TM~iYf|yWsdLJEWou6^ec@F% z@8P^Bzrq^&);)>(qGEq^n&q!auT3vpoP1{wxA%(|9(niIfjA=3&t7=ut#9hfJb8EK zqLx0-lYcc?t(K{l12wR{nK9{(ICF`Qr<{NF1w+M39k*vQCfK}@vc5Ixcv3^(wkJQY z@W}d}EH*T95B{_z`%Q-DylzITs7bf3vzM5tY)wk(Yg@7@`^^gPtfLIFrJjGa`K(q= zssgGG4Z3Suc17hY(2!Y6zMAsLih8b=n7LRf^G(!DKE1d<>;G$RN-jIochk_i?)XRE z*QMb<8}r#N1LH)s#5JNMC9x#cD!C{XNHG{07+UBWm;zCVk&%_Lv6YFTwxN-gfx*G3 g#7`(1a`RI%(<*Um*uZ|%52%5`)78&qol`;+00WfknE(I) literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/m2_win_en_toggle.png b/spec/gb/test_results/screenshots/m2_win_en_toggle.png new file mode 100644 index 0000000000000000000000000000000000000000..c2e3994ffad587e1995669dba6d56091bac2fe10 GIT binary patch literal 3538 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 A*Z=?k literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/m3_bgp_change.png b/spec/gb/test_results/screenshots/m3_bgp_change.png new file mode 100644 index 0000000000000000000000000000000000000000..5c7b94814f7503b35d09a48683bdae1e2dea36d3 GIT binary patch literal 1104 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 literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/m3_lcdc_bg_en_change2.png b/spec/gb/test_results/screenshots/m3_lcdc_bg_en_change2.png new file mode 100644 index 0000000000000000000000000000000000000000..4027b9cefc1ed19e47615fb18a2696d6457ab21c GIT binary patch literal 3017 zcmeH}ZBP@50fqyfqH;J&tF|C`is&f^)Tuy?VEh14nIm!}M9IbIDSE~OIU%gTCCi;s zQH#hZr3E2W&kH0Zxr0L>i&-jSIVezp1^Ea|u^3rGRz5<=$L==xF>^OJcCIs>_D_H8 z?#%w!*?s1H-ghq@hzxgG?6#Ojqq*$c8~TC$T4F!0EnQ$gquzB5G@A3ZeW5#9=WdSw zAxgO}Xkl0-b?5l?OItc@tZH||{Cs&~lK4R5!}`qUTZndWC24LDbF;av)mQvH3`%O_ z1kf*_QhHMNQ`joWRrJK|w@9L)F9rw9;bI=y1ITKOd;-xNM@J`u03&LmaEu2#j&8xgvg%)MRo7du1lb<^}svdk^F( z=>te8%lkVU6%@7_8P#--=$`hl`Cr2H4O+9@0!=}a)bD2xi8@Ih zasu?ezgx9gX=l;RaAn<*FEItX-Qjv5* z-l9F{*1cCB?LJ^sJ;ap_2@n2mV>Y7kay71;x6%C>JheHln8oFzd~mw( zi_2UJLC1GbN9Ick`5aL$iQP=rsW7|^Z>G4zCyno0Pdcr!qLqup{pRKr-yK2XD$Qxn zUQ>)XAp{#JGnQH8smazPGWGCU`MvyEXWfG4^&c!RfgF6SCh^KBag?WkjPi)uj-*wX zZl~eRK^7$j3dE8XFx{!j{4+AVWpM>}2x{ltZ638KQ;(xubKH|M=hAe~O7c;40i`8) zhd5xhxN3E;37LcOX+yZ9Eb|0`)TuFf2m4~Wr%Rh3tgkl|w?8AEK|UZmyb9(`Ngv_{ z6k)!|YhiCylvf3b%xZ--`i|86C2Y7;$Y?ZlgFUt)gM|B+7NRbcMuQt$9D}R=%%kk+ z>H0mZ+9;Mydsk*P(3zuP8Y!FT*Khh9gL^rq+ZWX0^N&YX6JM1KcY{WbHBTgMX>V1@ zHG4r}qDe2{wVKC%6Jv*ap%DYQO82V*Dah z_S)S`aH60dp-r!=gB_wmc%kO7)KYV}Fx1Q%`))7?yXS*GgJe{hSrlu-R&_mPF4QkZ ze4lG|KHQPySC6fj=$ij?W8*kKki@vHlz~*1%V93um!r30!S~=Z3u|20yOvtD<@|hb zUFm*~haq*#Zy;O6ys=_nbfpLwQDLPs)u+j>SuFus%BH4C@zGyHfr8NP7gFZ)=y=DU zFhFgPKGs_B0=Sb%8ar1j$zMntsy=rpLmF7~GN;|h_AP-v548VY z@3}9C_rB5h3wt{;##S$qeJ^`z9nTVm^2~C&j2JIq*lUhbisx|iAOBUE?Uwt}$hiDA zD7n;9q_l2KsV1SPcrrCWKWxx>JJM!-53d+1b1_&rPk^4ILBolpbQr(O)&g^z(K*G* zp4*Hrun>aRum~->ltn!Yn!mi7l!Gj(_08(47VL~4_&&SW#n+CBXNpRH$Zz|5U$c+N z=RNY>0nEE)gR&;&n4){xNVH-@Izwhses&v|k!j#@`(@DxQJ>$mvE<1%EtzMkFjAME&261&(!@D(!y>xh0!;!W!M9A^l{_lJfMy zJN#*F9xS`vhbqm{PXH5+N!vQD*34FYU+J<#FFNeQl+n6~i!yXnE%^u-gHMx@rgp`X z5%J-*v93+`XOUO6UTdsLx~^a%Fdb&UcJ>0Cm@w1?Z?Z=&W(FtxD%8S} z)yNw|_uB<0oXR48ycSJGP%T;TbXv7xP3JGM%}(}TblSeK$WZ03BVYUzqMLpT literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/m3_lcdc_bg_map_change.png b/spec/gb/test_results/screenshots/m3_lcdc_bg_map_change.png new file mode 100644 index 0000000000000000000000000000000000000000..648ee9ef536c383567191e1abceb0652aeaa77c0 GIT binary patch literal 1010 zcmeAS@N?(olHy`uVBq!ia0vp^3xIfn02`3Jue#?j0|WC;PZ!6KiaBp@9n8CJAmDoO z&wu~z4q~xa3|wpjx3RsS1KcT1mR%ulkI^%|KJU7#gFPIIajeEVzbl z!6`|FfY+*wmk*&Ro8~P5Rl13hY3m#yzNePpr2urw-oIQnsscdw<}^6yN-G4MR^!yhbfcLI9nq3Vgt&Wa5Vr`R~IfJ};MV%Tca%$T*8_rVkg zpiixQ4uIveerkURZU!pM`YH5egE>=p1y+D`?qp%is$^rnQssGIO9IFq8|IpyjqPCdpV)rn zy}J$a3h#r9|Ls*3uz2P+iw|czFvy=8A1Ha32UKux9^*Q;$F~&)9TZ9>4osJinDbi% z7)NFf3#I|Bd@97U;;AH0z-t3WfBp`jzQep9K8V*?ssMHJoo5I?{Bi99klXnZtjf(X zwPjl}`tx>ztm7@Xeg|aTG@vHtPcJusoGuG=f*uw(zGSh{)c}h70tR4a-|x85%?OFut3$8<;FYYS?y2P1)_N+~Dx4h~e_iSYWt- zf*s^BU`$R+H*i>cn)63l0#H2m9z*%r*uT~+0v4}7Nj}Jmc*oTTv?`a;=lMDz%oy9I z`XG8C&{+^-ef`d%P;f0byOK%(ec#lZj&vdfNKOq<8x8f=yM1UckJ};OXk; Jvd$@?2>>-OeGvcv literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/m3_lcdc_bg_map_change2.png b/spec/gb/test_results/screenshots/m3_lcdc_bg_map_change2.png new file mode 100644 index 0000000000000000000000000000000000000000..4027b9cefc1ed19e47615fb18a2696d6457ab21c GIT binary patch literal 3017 zcmeH}ZBP@50fqyfqH;J&tF|C`is&f^)Tuy?VEh14nIm!}M9IbIDSE~OIU%gTCCi;s zQH#hZr3E2W&kH0Zxr0L>i&-jSIVezp1^Ea|u^3rGRz5<=$L==xF>^OJcCIs>_D_H8 z?#%w!*?s1H-ghq@hzxgG?6#Ojqq*$c8~TC$T4F!0EnQ$gquzB5G@A3ZeW5#9=WdSw zAxgO}Xkl0-b?5l?OItc@tZH||{Cs&~lK4R5!}`qUTZndWC24LDbF;av)mQvH3`%O_ z1kf*_QhHMNQ`joWRrJK|w@9L)F9rw9;bI=y1ITKOd;-xNM@J`u03&LmaEu2#j&8xgvg%)MRo7du1lb<^}svdk^F( z=>te8%lkVU6%@7_8P#--=$`hl`Cr2H4O+9@0!=}a)bD2xi8@Ih zasu?ezgx9gX=l;RaAn<*FEItX-Qjv5* z-l9F{*1cCB?LJ^sJ;ap_2@n2mV>Y7kay71;x6%C>JheHln8oFzd~mw( zi_2UJLC1GbN9Ick`5aL$iQP=rsW7|^Z>G4zCyno0Pdcr!qLqup{pRKr-yK2XD$Qxn zUQ>)XAp{#JGnQH8smazPGWGCU`MvyEXWfG4^&c!RfgF6SCh^KBag?WkjPi)uj-*wX zZl~eRK^7$j3dE8XFx{!j{4+AVWpM>}2x{ltZ638KQ;(xubKH|M=hAe~O7c;40i`8) zhd5xhxN3E;37LcOX+yZ9Eb|0`)TuFf2m4~Wr%Rh3tgkl|w?8AEK|UZmyb9(`Ngv_{ z6k)!|YhiCylvf3b%xZ--`i|86C2Y7;$Y?ZlgFUt)gM|B+7NRbcMuQt$9D}R=%%kk+ z>H0mZ+9;Mydsk*P(3zuP8Y!FT*Khh9gL^rq+ZWX0^N&YX6JM1KcY{WbHBTgMX>V1@ zHG4r}qDe2{wVKC%6Jv*ap%DYQO82V*Dah z_S)S`aH60dp-r!=gB_wmc%kO7)KYV}Fx1Q%`))7?yXS*GgJe{hSrlu-R&_mPF4QkZ ze4lG|KHQPySC6fj=$ij?W8*kKki@vHlz~*1%V93um!r30!S~=Z3u|20yOvtD<@|hb zUFm*~haq*#Zy;O6ys=_nbfpLwQDLPs)u+j>SuFus%BH4C@zGyHfr8NP7gFZ)=y=DU zFhFgPKGs_B0=Sb%8ar1j$zMntsy=rpLmF7~GN;|h_AP-v548VY z@3}9C_rB5h3wt{;##S$qeJ^`z9nTVm^2~C&j2JIq*lUhbisx|iAOBUE?Uwt}$hiDA zD7n;9q_l2KsV1SPcrrCWKWxx>JJM!-53d+1b1_&rPk^4ILBolpbQr(O)&g^z(K*G* zp4*Hrun>aRum~->ltn!Yn!mi7l!Gj(_08(47VL~4_&&SW#n+CBXNpRH$Zz|5U$c+N z=RNY>0nEE)gR&;&n4){xNVH-@Izwhses&v|k!j#@`(@DxQJ>$mvE<1%EtzMkFjAME&261&(!@D(!y>xh0!;!W!M9A^l{_lJfMy zJN#*F9xS`vhbqm{PXH5+N!vQD*34FYU+J<#FFNeQl+n6~i!yXnE%^u-gHMx@rgp`X z5%J-*v93+`XOUO6UTdsLx~^a%Fdb&UcJ>0Cm@w1?Z?Z=&W(FtxD%8S} z)yNw|_uB<0oXR48ycSJGP%T;TbXv7xP3JGM%}(}TblSeK$WZ03BVYUzqMLpT literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/m3_lcdc_obj_en_change.png b/spec/gb/test_results/screenshots/m3_lcdc_obj_en_change.png new file mode 100644 index 0000000000000000000000000000000000000000..07c7952d9e723787bf4f8b54b2e57c6f0dec1faf GIT binary patch literal 812 zcmeAS@N?(olHy`uVBq!ia0vp^3xIfn02`3Jue#?j0|V1%PZ!6KiaBrZI`-W*5MXfp z|Nk=Q1ZTZAg_+BAI%7+Dc68gC9+Np#`IoVyD)uisLyRj!qYfj>CKiDcLJkVv#BpBN zGsORo`MI3o&ol<6NG6UHPK61ol@3tOPG*)(_a>>gIJCkgQ#qMZJmn^&GO=vB!^jjl zd!?{bLg{SzMpl7{xtszgjDTX(6db0d8#r8xW@r^yv4MqgQ<*&zx8jCXcU3zWm{x;S zs5vN{7GgQ^iHGgPEuIAeOsjAE&H<8Ee*_~q8n4R4HE}3xkP=n^+oI#xpmRQ(w(5;(*PP1tSI&Z&FJV#@w>^^20TlRwR+2sw}4jy<_#thUBWGJDl z4pwV9u&PY`z}SbtO9hQ&agNvMuB)eUswusOShv*4O#L;TD#u(4;qF!ar1{()}n%S@mPG?#%L zJ16&-?1Ef_4y@MFLc@EgvJ9l4#U2BHs a+WmZHQ1L1q`09elF{r5}E)D)+Ss4 literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/m3_lcdc_obj_en_change_variant.png b/spec/gb/test_results/screenshots/m3_lcdc_obj_en_change_variant.png new file mode 100644 index 0000000000000000000000000000000000000000..07c7952d9e723787bf4f8b54b2e57c6f0dec1faf GIT binary patch literal 812 zcmeAS@N?(olHy`uVBq!ia0vp^3xIfn02`3Jue#?j0|V1%PZ!6KiaBrZI`-W*5MXfp z|Nk=Q1ZTZAg_+BAI%7+Dc68gC9+Np#`IoVyD)uisLyRj!qYfj>CKiDcLJkVv#BpBN zGsORo`MI3o&ol<6NG6UHPK61ol@3tOPG*)(_a>>gIJCkgQ#qMZJmn^&GO=vB!^jjl zd!?{bLg{SzMpl7{xtszgjDTX(6db0d8#r8xW@r^yv4MqgQ<*&zx8jCXcU3zWm{x;S zs5vN{7GgQ^iHGgPEuIAeOsjAE&H<8Ee*_~q8n4R4HE}3xkP=n^+oI#xpmRQ(w(5;(*PP1tSI&Z&FJV#@w>^^20TlRwR+2sw}4jy<_#thUBWGJDl z4pwV9u&PY`z}SbtO9hQ&agNvMuB)eUswusOShv*4O#L;TD#u(4;qF!ar1{()}n%S@mPG?#%L zJ16&-?1Ef_4y@MFLc@EgvJ9l4#U2BHs a+WmZHQ1L1q`09elF{r5}E)D)+Ss4 literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/m3_lcdc_obj_size_change.png b/spec/gb/test_results/screenshots/m3_lcdc_obj_size_change.png new file mode 100644 index 0000000000000000000000000000000000000000..74d783189907a57276accf6d16a51b5fd3c3ea79 GIT binary patch literal 1424 zcmYL}Yfuwc9L4WG5|Th5HH0#N5>P=33NyA6B7_C4Xdfo6V2UXZMXbInr zxx{>=4SM(_g1dC-%@d(3J~r1xM^BB{{KeH(`UZ~iZkW{exd9%Lh||10BLeX9X?csL z8Reu0N8(*F34}KDJc$0)LcH9<vzCsvKUBY->=a75WKq0grJ@g)+IXdW}Tz*S3O0Vvt<4SjO=rdku!N-rS&`~ z?Z`Dy)LUk!+up&Tf`$fK*db@?kW06z4Zccu5rj6Jhvi~?QVgL*Inz?ao0m%F3?Qgc zIN>hHbasvG*bChg7C_pOm*5=Ge79x>KtqewZj!g_ALyo+xOh-Y_k*?mA$b3#TZyv0 zWJD30Ot<&%(YZnHSo*jrSSKdDVHoJcYiW}x)qk$^;l{Kf& zR>iFVvC(bKV^x}OSz(eCBg!aI%!U(5(7mC2WO}jhybN9Qfq4X>yP=b=k^{KLW*kJ^ zG4i503d477A-#RWxLVi`t~7cOFS^Og)dGq1=LH52mg6u(!4_*KSwslt$iaqHGr%z5 z`MQrqwZ zi!#H4ZECBxpct_Dbst30nu9?t*i{pXcXn9HZkuluhv!agi_VRuSs#m_!?R79_8w+D z7*;8g%{{~hnrFqx-x=~xabRFK5{BkmW>%`z!>2<2vdpj4%P}TxdywThmmKpu&g49h z2H;)LUDq&)_z*a#h@o+^N2u(^$`r}=Q3CGRe-^a9>+Y{RUI)_3=yJtx8ljP{tUEll zog+CmFR)(4Qq2eGZgL9cwLOZsJe!@++o6?c@*V~dd&&-0w+J;XereTU@J&a1vRvF^ zt_EOu(l0VH$wVHYS-Mv?Vy)L#QJKq;Cdm049QJC*uj zzm4DMD$%fU4*gDeDUi3Qo0MHt|1K0?eUm!XGx%C2Rb@PPK2!C1J>`h=AK~Xg>l~nD zenQEdgMOEFy{HzH#DT!^IA1!HgG#`K>fNBc5nmv!)Ncp!j!}@1IM4ZsD$mU_K}BF; zlI8Z`CdVXmewu{RDA1pYL}>ps`)etXLmugZzA4T!p(Md^EB$$PGhS&@!^ZvMymB5U@B@E3LeKmXtE#ZcZT zR*;bD!?54({=4m`J~LMyW4pj5aKn->p)6q)yVQ!;g$%263|R##UfpI~Q*~0I;Xv?P z;fb;d3)Vjteemu-V~A)S=cH1$h|+GpE2TA@lWN(vSMPh{Ewx}BNdCV4KlVL4owhQu z#2sT7NN{Cn%$~{2vhJ##;7vxhUtF`9Sze@_xt_2p?_%SwV=NbB6h5rm$hd`xrR-3A z>+ceX1Ac_=c%!?Ffyp0Lr-;IW*&n~c5OjDD1vEE%tG`QGL*u?f&lnhYNq#n+0d}Y%qX|35p6nckwG54OKZHNDlb!LZ zk?oaLov2=Qw24 z_!SOxF)&>>V&{liU8}T-ndghB89T>?voqEo3;{W`mve!o!-Lpl<_uPjH!b_QzP~&W z!fz6A{p0F_o6$auEb~xxYB)HUKb)=*ncfh({RJ>ErYqb`FWCM3^u8sGucZE)N@zPI zq}DJnhOr3jmGVS8r&vMd=dCe|Y_E%Q>SWenC-?okarpg;|{m1%)5sXaQkC{0( zC^a0|dWMndde;1oHw?_Rtw1|UjGtv2fE}956!8@p6W+j(Zv}Z0oM>eCb8Y{1mhXX9 z4HL(np83pwerY!x@czwGV}2kK80^3#C?bKLkYW|;|M1_i!D{=l^NdVuwqTFgHC3L> zS5$#c1f||DV!;fJ%Ypul{+q^LAZo`5%mU{b>;8&5JXl)Gv`_lM8VBZ5pa-~kF8KaX z^|&UPa8=g|7$>SH^dC_4qk3;BTobSs!qSPb_X=xZCqn$_K1*2E#07mV2gV z4Pgxj{C=!1n9t|)6=(y{|I_y#6>)f=SqyX-LMIbTi7>95`!T+GBNIy)mK^(wTj$As V_Bl1YJAefcgQu&X%Q~loCIE7J1F`@B literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/m3_lcdc_tile_sel_change.png b/spec/gb/test_results/screenshots/m3_lcdc_tile_sel_change.png new file mode 100644 index 0000000000000000000000000000000000000000..495fb74987bdef560ced46ade784bf41e4fb402a GIT binary patch literal 1018 zcmeAS@N?(olHy`uVBq!ia0vp^3xIfn02`3Jue#?j0|WCxPZ!6KiaBp@`DV!&inIpi z8_hnWCTwt|*~E8c-pXAn?<|FzEB{H>?wR)G>+{6;`=8b`tb8l1w7?UJlF#w!n1PrOV>&5th41LBL$sg~3 zuWoe)dX!gS1<0zMER0!|Y|K}xJP+g@1X^EO!S>@5b2muIl@Dw`^4?{Fe8TqO#qZTD zE?Bh9Ziqdk2#kauJO*=j&j#AMJ-uP^!3rxCpf)~cpi#}m^OYJLPO))Z*#r!~m?nm; zHqDG#vT6xm8$iCg!@ysE@UWsF*k%i^A=bzE#1Ij zEyznkK%X3zez0ceO`rna`HX#RPcA2d^jiTn#bVKPnKMFH1Lz#T{D#fX(m*PI@f4V- z%;)L>Iu~S94rU;R&1ncd1ayhkJ%;qNvB2b@WydVTd+P2&kakO;9vw_Q*PgQ2$VvcR zH{JX|WO4X^enysayK2}hq^9j|QU-b|isACk)mYq@$yuRg0yJ(}F3^3K_Uktsh`fA` z!JXOI+6E-b4h+~|^&AQX)6k-9*1U$z2Y-Im{r~HC^gq!TrnSsA*6Ixhf|miqB(LuO zKA=ItdE5_P%wb^i-v|tQP&xr7Io<>iUS*K`J9o1qTh*OyjD6=g6+TS4 s4Yatq*t7*`aUMTd>-;pd1o@BY#>^{?OkGtMfq9z2)78&qol`;+0JNrj(EtDd literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/m3_lcdc_tile_sel_change2.png b/spec/gb/test_results/screenshots/m3_lcdc_tile_sel_change2.png new file mode 100644 index 0000000000000000000000000000000000000000..b50492a9b4e44f606b4f4d3ace9cc0c6944c8469 GIT binary patch literal 1640 zcmeH|=~Giz7>93I0)iRQ7OI4GsI3(dtilC_kff2ww1rw$p$uuEO(ug0Rs~!_A}BLf zkxc8-QEV78mF04ch;S7ogw`q+m8B#s0d6d6fHVY1!jh1rx3yxc|3W{Ub3U98=Xaj> zdCxidNgSQv;^_ha03mi4?Ne;6z=qHbkIm@%+dl&UJ~);}Nh~PW4p7d#{oYE)Zt0WH zzTO-WaIWuCj{nMcwtu|PN^rHi|ES0}ammD0^_MLy1lg8Sefj43d)5V*6en*p<&&b4 z7cCPLW#efh6-h<8{Zo{>UR||y5SkcU6XJB!^M4DZ2P~STC$UYJXH2bJ|B%3knl(Z}3*r`Y&G;!NsSiJtjQ_4M6o5 zjn>`1&@uOn`NZj;R@j9UsOV$Jm)z85R-iZl8Mt2RYsO$N(g_9xHeGe*BbQ^Hkqbj(7GZ6i5N{4r0(k2U0Y?#(Hx; zZzGwDrVM|+1H;+UEHA7zz6IMR{C>rcj~}9ReAi@iJmYhAlB3fl0#lD9iv7)n9lLC< zkwmDawJpceAWb$$TaFr0QOh8eoZg9BXaWsdP?%#u%7m*&F}%U1(mGR2>0E-5*!Ndi zHCS2ey#!okb&s@mlpN>yVEop|!K!CWjbD}-zcwmd4zdlZ6fN^V=s1FQX$*$Ww)rKx zSwhG_yqpp3E@;t1#`3Ga@1IAy)pyHFSyOX+eqme0q-6WRW)4V3 zueVhPSqRq-lONDtOZEhRbAZ`c@h?`rpe(&uhm4RQ!zGxn=x5uiL=vlcKbY>!3dD}( zZAA<1?<@Fb=XJjY8E!r1EQWcwYazBsY+~{eZ z(>}tyg0`f;0yy;nlRQ4NiB~565VmEoF(O@ zNU9b}6AhY}Ja~?8TAXZ)&Tlv&X!2>njm}$H7H97cop9dU<)(FVy hc5bZyH!%nrzBS+!>&h?wR)G>+{6;`=8b`tb8l1w7?UJlF#w!n1PrOV>&5th41LBL$sg~3 zuWoe)dX!gS1<0zMER0!|Y|K}xJP+g@1X^EO!S>@5b2muIl@Dw`^4?{Fe8TqO#qZTD zE?Bh9Ziqdk2#kauJO*=j&j#AMJ-uP^!3rxCpf)~cpi#}m^OYJLPO))Z*#r!~m?nm; zHqDG#vT6xm8$iCg!@ysE@UWsF*k%i^A=bzE#1Ij zEyznkK%X3zez0ceO`rna`HX#RPcA2d^jiTn#bVKPnKMFH1Lz#T{D#fX(m*PI@f4V- z%;)L>Iu~S94rU;R&1ncd1ayhkJ%;qNvB2b@WydVTd+P2&kakO;9vw_Q*PgQ2$VvcR zH{JX|WO4X^enysayK2}hq^9j|QU-b|isACk)mYq@$yuRg0yJ(}F3^3K_Uktsh`fA` z!JXOI+6E-b4h+~|^&AQX)6k-9*1U$z2Y-Im{r~HC^gq!TrnSsA*6Ixhf|miqB(LuO zKA=ItdE5_P%wb^i-v|tQP&xr7Io<>iUS*K`J9o1qTh*OyjD6=g6+TS4 s4Yatq*t7*`aUMTd>-;pd1o@BY#>^{?OkGtMfq9z2)78&qol`;+0JNrj(EtDd literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/m3_lcdc_tile_sel_win_change2.png b/spec/gb/test_results/screenshots/m3_lcdc_tile_sel_win_change2.png new file mode 100644 index 0000000000000000000000000000000000000000..bbc25920a4324a3d1ce0facab247c5ead70e9d3e GIT binary patch literal 2030 zcmeH||4$QV9LJwKT53U*Srw;2(1b5eCv_7gAQYT{sA*LU${5-?1}uoyX(?$bt-5HG zO^L)w6sSVB4$J%0cEY6hL^$Y4kwS_jLwDjxkim-tG3;W^j`Qa|lbMMdV z{eIu+?bOVWpv6G|fRL=sn|5OB73``G4aDyHsE+#p5bk7cO50UlH>}A!`*K}qs`9`m zmwTc~bl@e&hD9_+Nqe0i(BnfMo)@*me9H4K88@Bty==}vlk|4IT#7zDck2bOZ9C_L zcQmtNgZHGzea^{L57mUThEcZMQBjt!8C6M*6|Futmx;x+k_&-9DfjtG1{$X&Am${`v?h*pie zueXoNzo>IJ$!TRYKKw_FZ#LubakdMRt>mkjrG1%}Bp3cSDGw@0uf1-Ey3Y#V)Y}SU zkTwgNm~ppC#9D~x+y#yy)HXJ#okI;nF0JK9BP?2^EE+K<$X2rj`2tEse>h?cbYB3y zBF{ zz(rkcrWsI5{7}A}g0gu&vh+T*7V0d4)$veamzt?`n_Zs4n%9b1_1bdbcUE?Fp-LXb zSDNHQ5|gUoP(XsTGsIx_Z+{=ElPmSt98g_-DwSw3hH2X1+TlD5STba%ekTl^3Fn5d z@W~__lHoIwq0J$v7u16|xg(83F;B?xd}w$vr35?doQ`FyV)*FT&`XUJ;>gm@-1ag8S<`R?Qgr^vE_sd4 zj2pLm_jSdVE5~}_0gk}z^9VXqIy84*!+S`9WOp=rAES=(_t{rdKHIhsNj~Vl7%8M47rvAK^FM#&dKR15uozJ@4-x4$w#8{o%uvZg z3`W;FzQi4tnsbyumffWZuy)uTGc!Hg2xJcCz4rvE^`T;r;STp=R@Ou#Mvo?Wx*p1H z$Abwy1sVZ_g;K|S+X(*98o8N@(A|zW{&g$wSW=7}8^EwzadwBb(R3NBo6Z5Rlfvbe zx1HFDXWYd4V8#1=Fq^=zL6j)I#H5>t7{_#@N@kUavjkC&m4YJw&h1>-a{=h$kJZgX zbaCy)@L!YVtm(IvK;{`mFwJcnLBu7XkfUag8CU+ge*8HWWL#`@#d3ZX>CdNg2lAnw zY0T;%6MpX9Q)E}AdLg~o0L$Hf0H?~K3~kh5}5V(+g9nz8>l N$V#Vf5^ba({};?p@R|Ss literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/m3_lcdc_win_en_change_multiple.png b/spec/gb/test_results/screenshots/m3_lcdc_win_en_change_multiple.png new file mode 100644 index 0000000000000000000000000000000000000000..145bd1b44eb90e854c6be451620c45864eefe5a7 GIT binary patch literal 5295 zcmeAS@N?(olHy`uVBq!ia0vp^3xIfn02`3Jue#?j1A|DJr;B4q#hkYhf$I)i@SXnK zziIlMSe^~`ZeKj27O5_|)<0|46wgx|d+pBe|9IX$>tB7y!|MqjLv6}lhy1=Ub@vzl znfK=gmb`uw_kZu-|M%Pf|B<$EZ{766gX7Tp9XkBm`cAWp$zMJBDD>}%`fc;GCUVP7yUE+V2 zAyn?_kNXn#6~&z&0&TxlPv@Jqezjvq{WrEVatdqu-~18epXUF~ZE2pv#CyRt7mKg` zU-#8I`5&Y8=PPV}((aK*H|sB`u6cU!T6e*lnpbQsHKkwqZCC&C|89P2_rLeA>Ypdx z*crNKHDjoa-_HY&LhElxdxW`9oByg_?71Dosz1)>L;r-z@4hzQ>i_-6hd8%#y*d=0 zdZlCij{|M9-M8{b#%zD5dGqc5?7vHY9}nJL`1n|F{?X!JM>;;dKk`StVw>Mq{@9(~ z|EK)CE9tuZ+`F&$Z~tHZ-~Pa8_>6|nX!;q=U!&#EX!$%^e~s4fqwSy3_TgyzaJ2nA v+P@g>e~tD}NBi%i;~S&nKf`x?te$ZxJNNn3bM4)M1Cb1#u6{1-oD!M<_$jcd literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/m3_lcdc_win_en_change_multiple_wx.png b/spec/gb/test_results/screenshots/m3_lcdc_win_en_change_multiple_wx.png new file mode 100644 index 0000000000000000000000000000000000000000..bdfd6dd3c7c28805a59f0dd634b5bad85710ed98 GIT binary patch literal 1042 zcmbu;Z%7ki9KiACT()t{T(uyye73wGHA1#r&HT4Hb7yKI1!_SKmPKlWA=zB#4DB+a z&ND*E!eTFaaha%*QiwGOt<8*R*@AR*oiFSk9Rt&q-92xkSG{4eP2E9}(i&at`UMKSa;#9;% zGCL46i0_1=m>sXK|E(cn2TUy*Hm2f6uA0QS0KDi0K_R}N9Mp=+t|cc150u&7y5d->qNCGT0aNd1z>Q|$7| z^&H|%#QRY#a}~H@e_5bkoaSiTR2?<$4!H7oT%i5)9G&0qq^8||S2}-5paaVsZSAk4=A!RhI)05n zPkp;bTfRD}@6i+4s)j<*H(vrKXR^SfL-8IS=x5$6g-P5|^J|W1PamR=v)1m)+;md! z=iqg(rY0C)RIV~E(9+v`A_`Xf)V~zA%Xu}*x1n5v@(U=pqFk5K7Gp10#WK#LNd!hz x-k?c928GsjdF+F;ccmXupR^tEKAExRSF;Hv?!(7Ia7Fs-fGjAr^;@gY{{iay$-Dpn literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/m3_lcdc_win_map_change.png b/spec/gb/test_results/screenshots/m3_lcdc_win_map_change.png new file mode 100644 index 0000000000000000000000000000000000000000..648ee9ef536c383567191e1abceb0652aeaa77c0 GIT binary patch literal 1010 zcmeAS@N?(olHy`uVBq!ia0vp^3xIfn02`3Jue#?j0|WC;PZ!6KiaBp@9n8CJAmDoO z&wu~z4q~xa3|wpjx3RsS1KcT1mR%ulkI^%|KJU7#gFPIIajeEVzbl z!6`|FfY+*wmk*&Ro8~P5Rl13hY3m#yzNePpr2urw-oIQnsscdw<}^6yN-G4MR^!yhbfcLI9nq3Vgt&Wa5Vr`R~IfJ};MV%Tca%$T*8_rVkg zpiixQ4uIveerkURZU!pM`YH5egE>=p1y+D`?qp%is$^rnQssGIO9IFq8|IpyjqPCdpV)rn zy}J$a3h#r9|Ls*3uz2P+iw|czFvy=8A1Ha32UKux9^*Q;$F~&)9TZ9>4osJinDbi% z7)NFf3#I|Bd@97U;;AH0z-t3WfBp`jzQep9K8V*?ssMHJoo5I?{Bi99klXnZtjf(X zwPjl}`tx>ztm7@Xeg|aTG@vHtPcJusoGuG=f*uw(zGSh{)c}h70tR4a-|x85%?OFut3$8<;FYYS?y2P1)_N+~Dx4h~e_iSYWt- zf*s^BU`$R+H*i>cn)63l0#H2m9z*%r*uT~+0v4}7Nj}Jmc*oTTv?`a;=lMDz%oy9I z`XG8C&{+^-ef`d%P;f0byOK%(ec#lZj&vdfNKOq<8x8f=yM1UckJ};OXk; Jvd$@?2>>-OeGvcv literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/m3_lcdc_win_map_change2.png b/spec/gb/test_results/screenshots/m3_lcdc_win_map_change2.png new file mode 100644 index 0000000000000000000000000000000000000000..ff3ab9ec4c913253be878103812868a7085d2e96 GIT binary patch literal 1600 zcmeAS@N?(olHy`uVBq!ia0vp^3xIfn02`3Jue#?j0|Ohor;B4q#hkY{V%NRV;BkF; zre4GLmG@uo;c5V{C-tuNE)M9lJ?5S_y1e|`&4~?npR!E&hO`+?>~{hc%c5@ zrTRG8_`CW12A>u+pTA~5fB!xEI=&a37msXyynfyEyYjr7%4$UBME-jI`TB45rq}E4 zgGA@^Zn^joA|{;Q5LjDQ^YvFS;|}loSFe6w@1O7FKPOg0D*o<%ZHCR?&Og`xS}p$a zWqQfP)jt=1p7XWr{rvOs59@aZF?`6|!>IOoH~Y+#|26;o{#_KSx_@Tz{VOm3F-N@F z&Mr}T@nHIEpxXEKocl5wZ{Dx|9RAh4d5x?*Yx=Z*KmUCFr>|pToiAE%cERm<`b&^P z`yXs8{_Xu2!tn0W{9V8PeU0~cbnnNjf3b`+w$|I#ue`t8!{2^!cg$J&xzE>ZuLfGm z_5bu#riSvzyaqGx+n!JX221&;{%r!a#^>vj^Z&~qNZN0E;6yi{T{KW_)qj@{uH1ds z)}Nb?8Z5%xy?Qp*dQtz^{>xd=+jssFC=};&Y+8OSe677`!|pH3*X^sXO-T4%{YB<( z#Fl@z|L$fo$Of7iw7-1fygE1WowMw%&PV2d2YO2M-*jzOhWE#$6O8s(Pw)hW-uox= zxrtwHSAL7GzWtv;?Dtznx44oIH^C7if8^Wuj5{xE!uM|fx!CbqUw@OaH^`;+Nek~8 ze@d5L@#s9;trtIZ{(k=X`fnraxx6QtSDe>O?`u$eMsAyLbD2^NoHWSLtDEYVjH%i1MB&?Uq)w%WH=GXhkaVLF`;p+LTa5vQ@E?O-O)i z4`)bw3(SCscFKZl;{lo=SOxH2$lbThC7h%s_3&|wmAh-6V{*u>$$kSf@~ ia7t-Z0de8*iT(ZV3090f30r|R1B0ilpUXO@geCx;IvuwF literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/m3_obp0_change.png b/spec/gb/test_results/screenshots/m3_obp0_change.png new file mode 100644 index 0000000000000000000000000000000000000000..ecd80c86045aa870d1e0a9ddcfe1eff5cf6099d4 GIT binary patch literal 841 zcmeAS@N?(olHy`uVBq!ia0vp^3xIfn02`3Jue#?j0|T>wr;B4q#hkZy9rNxO2(UQ( z|F8Kog@+}8&y2+@CP-=d>b`?AHCbCMn$KR3ZwQFb|If&fFWBIq!^m+&2pjcFzM=kK zb!P?*NXapV&a zTm5AbYjG%DKK}rdzzRQRmZVIeQmC41>ls8^9JqkIu+PE~0!*Q%(-nZKywFr-GH^L9 z*!4``iQ8$xs?+liGzqMD_DRTvf$3@n&@5hoj#N&jBSmb?N3z%!D6p)`s$-I8Q4Bcy z)<40KBc$;6^9?Kt0j)r%Tw-X9n5N*MlWyR!=JNa_;y`DeH9s&PsP^bnwhA-$4XdC1 z=PLj9o=M=wTr}m+{2zfW&u`dU%&^+3mU#;=+p27~2mad*I*3XELj&k!P{7PN$gpwN zehyuZ##OU;H@FHg?atcAc#DlWG?wqcJFw>1s?~!{TQQVTC_=FPKTJ zcvi;n?HuD8H?&ZA%3#Ccw4m!YZ^0Fr1Gn?`gN-d=Pk1kLK+6OeH9!|{Vqr|OY-UWl z%Xgqzfu-#FT?Y5FjC+>;1{r(f?s3K&*6bh5fYvVs#+!~~L&Uj+hKRe26#^{>*4crb zFz?$(=>@q48&)TR)rz61m1hSzcems4O4CV=PZT$ZKiR z|JX95dEO*NfxKqpisrQsAg?8qUY2^W!m=T9*R?n6Lh4p5%5St~|!0yPgCaa2B v#+2RQ))#Y!+X)Gw{j8v1&_RoceT)kxTl~HqxJeV3MHoC?{an^LB{Ts5kFqcX literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/m3_scx_high_5_bits.png b/spec/gb/test_results/screenshots/m3_scx_high_5_bits.png new file mode 100644 index 0000000000000000000000000000000000000000..43d5472e5c172201b5c7d85a5d213aad4cebeceb GIT binary patch literal 5570 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 T?I3zLi`gErBb@bV;?e&Cv$sM| literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/m3_scx_high_5_bits_change2.png b/spec/gb/test_results/screenshots/m3_scx_high_5_bits_change2.png new file mode 100644 index 0000000000000000000000000000000000000000..3d8983a8a9145cfa9492fb566b55a69e82874de2 GIT binary patch literal 2934 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 literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/m3_window_timing.png b/spec/gb/test_results/screenshots/m3_window_timing.png new file mode 100644 index 0000000000000000000000000000000000000000..5a9f5625e35815a25cb422cac40040b4166a5f46 GIT binary patch literal 930 zcmeAS@N?(olHy`uVBq!ia0vp^3xIfn02`3Jue#?j0|RrWr;B4q#hkZS4tgDSU~mom z|9`nC%fZ%;Z`&rATW+?zpvG_1{rlUu%6s$6_I>jxI-GC6=U2?*X3tNj<^KsdP5S;} zI(y56CpZ5+bW~KST=u`hk;8M|Q)YcG#goQ7%uXIfw+$2;RJQ;9*1TZip6T{~6dP3D zKh)>vP&m2y@8M=9k54}TKQ=K;vN_EjC*m+EU4o5e!jo-@4h)`o()gGUE*AIltDiIo zIB%-g@T8WP`MlNj9UPOs8}r!wj(P0laqqanhgHSB-HMg_5+7WBE+y{rDdunk!=yWA nJV2{+N13DHK;J|VYR|wK^ZwTwt(QxId5^)<)z4*}Q$iB}8gzGT literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/m3_window_timing_wx_0.png b/spec/gb/test_results/screenshots/m3_window_timing_wx_0.png new file mode 100644 index 0000000000000000000000000000000000000000..ac674580e8d1d266b3724a672ae8e4f35964804e GIT binary patch literal 1326 zcmeAS@N?(olHy`uVBq!ia0vp^3xIfn02`3Jue#?j0|U!fPZ!6KiaBquY~*AxU|@3m z9&gpClOXQ;Z}w~5E1I7AE=TTb)zxLpq?^b@<`_rn;>4@#redh7U z13Gprzx4gtYY8sF?;*eL)yO(4KAPusfA;!h4VOE?FRMS7^0anTYt`nD8ZsItqv>Qc g?+kQN5c7#?_W72-Tu+S_0n0%KPgg&ebxsLQ09k;rzW@LL literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/m3_wx_4_change.png b/spec/gb/test_results/screenshots/m3_wx_4_change.png new file mode 100644 index 0000000000000000000000000000000000000000..d12186776c715d2a01d89d65cee855bcd8ab9a9a GIT binary patch literal 5367 zcmeI0ZBSEJ8pm&h5~0h>iqsB>(7K9%Vs}QsA|_zzqOcN62oW#_TdjadiZuq693L*&zhASYbiAe}C@45GG*y-o})Q@~P=ggch=b8UJ z=XZW{N_V6rdV2VK007{*ZEHfRbFFud^FD6Q8D4egApk7x+LjQXR&+&^nRV{bMlX>) zXy5v;WuMoBErl1BhHeUdLOikQ>h8VIw(a2cLuwwFW|z7VE%&`qr3FJKh*(5E)eHZ> zjao|pe$BmIJoV4)CalLe(S&T<)SB zsMhzZJGGw^e@p)i#0GfSaq=IQlcU#=C=G;y1A9J zgRKJ63|pt(+E{mkMK&44+h|SqIv+gfxz(3D$Kr8cx}4Ok3;?U}H5K-R2CsU`UD@2X z$E6tmqrJ`2!0>j=13{Nl01C^XDFD#%=#6c{Hu#+oiO6=0)e-!~wma&Z}JO4oGJ zZ!$tA!$<;qXon1=**5jj+r9iBOADMawQ>%yN8GEcUsLV`KxauW$wuNTj&%AYr|Hi- z_^j(VrV;68sWmpY=cicEo?AzokwlItw$mZ9HmJUF*)bhb3yoqzG~FV7wBN3te9Pi; zZ%8LHR%C$h-lVHDl>N4NqrhcH-^y~H8aAG2ROwL8?3KQvL8OFQSekj8nRWyj%^@D za;l;5h)6(Zja(hovgYrf>3U#VNcbb#5f;WvHIwsMnCv5~8p;dYaE_sL#`Ghiur>Q` zh`cWbZk@5A_iT=c4Nc~BM z97d2wPo;;rt``d8s)0(#?)sk@+(VsTup)xD&WgaFnii(@jF}M0;U~HqDb zN30_2kI7jMcRFM0vx$v+LUQs>Q1u0lFv32P9ILb_FZtST$B@*RSU0WaZcBxSsLl#Ktf|L9)i z>cuSaekM_km-D^iA}cmtTZE9^UV4!+4Y+@)rX?nQB|2ouMVoF70e9XG|2p#I zTEfmd0U)ck(TDX?iGWE;(oH3z8-#OFA#-437tq0*M%&b#jSyR$1lhV$hvrj{#L(yjz{$gC%fm<7&@?QE*Gtz-g(3p|3kR)GfGg^k{PX68Z%dnt zqET&3#uwTanH{U_ z6v3HC$+3XC=?liwDFy9ya-=aRjTZyRV+Sp_QQG<^LR6~@N$IL``Nx^VE`zT14rNHT z3D{$Q(nFuVKQl%6PT-P5(KQu6AJ<@^2w`29sH6>@aZ+#1vi>CsHd2jOGfw?5C2(16 zY%e3WQ4MgPt~cFI7t^OpjBpe{TQt%mn0K1pen6%m=9Oazm2FIK&P4?1b3zp~`j~7Z zPw}m?$yArwI{FXauPj@#$9bES%$6;$_OZ-ZD3jQI#0({ae5*(iKV?xkV>+NI7+ zB$UR^0fdh02CAJdhdr=MmVA)rm`8~*D)!k*wRi2Syv-te2Jr@g2?&o~*Z+*jcAuvp zN|kzVE(%>*%3L47Xkq pBHS|w8%LzAqsoYmAoQTB`r{n8v*ZAKG%#H-c)I$ztaD0e0syQ%ptk@3 literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/m3_wx_5_change.png b/spec/gb/test_results/screenshots/m3_wx_5_change.png new file mode 100644 index 0000000000000000000000000000000000000000..8d68a492a0af96949394d3c97df99aa88624dda7 GIT binary patch literal 1195 zcmeAS@N?(olHy`uVBq!ia0vp^3xIfn02`3Jue#?j0|QHur;B4q#hkad9Q#5Ic~~6o z|JR5qa!}r=n0z#H;{0{@wj5Z=ukC%&`)c{VeSW|5E9O17zy1Gbcl`aE<=>la&VQc& zvi?WoUHgQ*yu5$M{_M|BnD_kW|EKjo9M^8G+qZo7*5B1Y0spUJ3gtcz^eZU6os zGf3MbU59zklWYI}cD~DBe)hA?|CRgF*!NBIkv;$U&(=EG&W@BNe&#E$McH58y}QVK z{=Kc0^PcBJjJanl0o1ye^~C3zHQ)IrJpUOHznS^&^T(B+ZO+F(zdo_U!|hecF8};8cJ}kie_{EIpKRn`+crO}w7VAFU~~R>)q94@dHr8atuNHt%`*pDTRLj( iXdsU!DH>;>zxlkGA0O;akq4GO3=E#GelF{r5}E*7jg>n9 literal 0 HcmV?d00001 diff --git a/spec/gb/test_results/screenshots/m3_wx_6_change.png b/spec/gb/test_results/screenshots/m3_wx_6_change.png new file mode 100644 index 0000000000000000000000000000000000000000..606181794349aece8f371861202f152ef7a75550 GIT binary patch literal 1216 zcmeAS@N?(olHy`uVBq!ia0vp^3xIfn02`3Jue#?j0|QIDr;B4q#hkad9Q*EC@UR5j z`>#=a@{GcPoRq1zqf|xWbGdXMe*E6mkQn~=-1_+Cf9oseJ+J=#pYPw#x$~`gpH$9! z|I(bf?ib_tcXxO1fBdgMKVjbUpY~79nYUj1^~>(=hL>}mSMHm=ouP7`{OiX|e_p5%NT>tC)vf9mBt zpb4uXCe*SW{`@B*pYfB8{k3qZDQwH1fgBLewtjDY@0rgw_b=G@ySSt+@iSj>Eh_$U z{QRkxZO?zM35RI-&C~Y$=f-hS+>RPpw2b_&pr zwttl>N=C}1yBvX`HtXN7HFMqp!(=x^!@YC^P)La>0PU|`>X=!O1=KO`U+RZjzptMF z+QIwxvxiHH+pCi1thHZF|9*|My!+Yad^tqJ-t!3+z>wE<0NVe{tNGG{D?lB#e@{Qy z`hERGpdHe`o=@yJ(R1Y`+tsMo*}q*VCVtoCgN!Rfq<==7+$5hQymTuUB(0P{;Q#`pwtx?RNu4TmFmD ih#rmT*N`0jUw@0~#|H;{+};37CI(MeKbLh*2~7a;>aSt| literal 0 HcmV?d00001 diff --git a/spec/gb/util_spec.cr b/spec/gb/util_spec.cr new file mode 100644 index 0000000..18f6996 --- /dev/null +++ b/spec/gb/util_spec.cr @@ -0,0 +1,17 @@ +require "./spec_helper" + +describe "util" do + describe "array_to_uint8" do + it "converts array to uint8" do + res = array_to_uint8 [false, true, 0, 1, 0_u8, 1_u8, -0, -1] + res.should eq 0b01010101_u8 + end + end + + describe "array_to_uint16" do + it "converts array to uint16" do + res = array_to_uint16 [false, true, 0, 1, 0_u8, 1_u8, -0, -1, false, true, 0, 1, 0_u8, 1_u8, -0, -1] + res.should eq 0b0101010101010101_u16 + end + end +end