diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_GB_PPU.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_GB_PPU.cs index 62ba83bb26..6842e7ca17 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_GB_PPU.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_GB_PPU.cs @@ -136,7 +136,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // don't draw for one frame after turning on blank_frame = true; } - LCDC = value; break; case 0xFF41: // STAT @@ -198,7 +197,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk case 0xFF4B: // WX window_x = value; break; - // These are GBC specific Regs case 0xFF51: // HDMA1 HDMA_src_hi = value; @@ -284,9 +282,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk BG_transfer_byte = value; BG_bytes[BG_bytes_index] = value; } - + //Console.WriteLine("BG PAL " + value + " " + Core.cpu.TotalExecutedCycles + " " + BG_bytes_index); // change the appropriate palette color - color_compute_BG(); + if (!pal_change_blocked) { color_compute_BG(); } + if (BG_bytes_inc) { BG_bytes_index++; BG_bytes_index &= 0x3F; } break; case 0xFF6A: // OBPI @@ -301,7 +300,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } // change the appropriate palette color - color_compute_OBJ(); + if (!pal_change_blocked) { color_compute_OBJ(); } if (OBJ_bytes_inc) { OBJ_bytes_index++; OBJ_bytes_index &= 0x3F; } break; @@ -766,6 +765,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk read_case = 0; internal_cycle = 0; pre_render = true; + was_pre_render = true; pre_render_2 = true; tile_inc = 0; pixel_counter = -8; @@ -834,6 +834,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // don't evaluate sprites until pre-render for window is over pre_render = true; + was_pre_render = true; pre_render_2 = true; } @@ -852,147 +853,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } } - if (!pre_render && !fetch_sprite) - { - // start shifting data into the LCD - if (render_counter >= (render_offset + 8)) - { - if (tile_data_latch[2].Bit(5) && Core.GBC_compat) - { - pixel = tile_data_latch[0].Bit(render_counter % 8) ? 1 : 0; - pixel |= tile_data_latch[1].Bit(render_counter % 8) ? 2 : 0; - } - else - { - pixel = tile_data_latch[0].Bit(7 - (render_counter % 8)) ? 1 : 0; - pixel |= tile_data_latch[1].Bit(7 - (render_counter % 8)) ? 2 : 0; - } - - int ref_pixel = pixel; - - if (!Core.GBC_compat) - { - if (LCDC.Bit(0)) - { - pixel = (BGP >> (pixel * 2)) & 3; - } - else - { - pixel = BGP & 3; - } - } - - int pal_num = tile_data_latch[2] & 0x7; - - bool use_sprite = false; - - int s_pixel = 0; - - // now we have the BG pixel, we next need the sprite pixel - if (!no_sprites) - { - bool have_sprite = false; - int sprite_attr = 0; - - if (sprite_present_list[pixel_counter] == 1) - { - have_sprite = true; - s_pixel = sprite_pixel_list[pixel_counter]; - sprite_attr = sprite_attr_list[pixel_counter]; - } - - if (have_sprite) - { - if (LCDC.Bit(1)) - { - if (!sprite_attr.Bit(7)) - { - use_sprite = true; - } - else if (ref_pixel == 0) - { - use_sprite = true; - } - - if (!LCDC.Bit(0)) - { - use_sprite = true; - } - - // There is another priority bit in GBC, that can still override sprite priority - if (LCDC.Bit(0) && tile_data_latch[2].Bit(7) && (ref_pixel != 0) && Core.GBC_compat) - { - use_sprite = false; - } - } - - if (use_sprite) - { - pal_num = sprite_attr & 7; - - if (!Core.GBC_compat) - { - pal_num = sprite_attr.Bit(4) ? 1 : 0; - - if (sprite_attr.Bit(4)) - { - pixel = (obj_pal_1 >> (s_pixel * 2)) & 3; - } - else - { - pixel = (obj_pal_0 >> (s_pixel * 2)) & 3; - } - } - } - } - } - - // based on sprite priority and pixel values, pick a final pixel color - if (Core.GBC_compat) - { - if (use_sprite) - { - Core.vid_buffer[LY * 160 + pixel_counter] = OBJ_palette[pal_num * 4 + s_pixel]; - } - else - { - Core.vid_buffer[LY * 160 + pixel_counter] = BG_palette[pal_num * 4 + pixel]; - } - } - else - { - if (use_sprite) - { - Core.vid_buffer[LY * 160 + pixel_counter] = OBJ_palette[pal_num * 4 + pixel]; - } - else - { - Core.vid_buffer[LY * 160 + pixel_counter] = BG_palette[pixel]; - } - } - - pixel_counter++; - - if (pixel_counter == 160) - { - read_case = 8; - hbl_countdown = 2; - } - } - else if (pixel_counter < 0) - { - pixel_counter++; - } - render_counter++; - } - if (!fetch_sprite) { switch (read_case) { case 0: // read a background tile - if ((internal_cycle % 2) == 1) + if ((internal_cycle % 2) == 0) { + read_case_prev = 0; + // calculate the row number of the tiles to be fetched y_tile = (((int)scroll_y + LY) >> 3) % 32; x_tile = scroll_x >> 3; @@ -1002,22 +871,27 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk bus_address = 0x3800 + (LCDC.Bit(3) ? 1 : 0) * 0x400 + temp_fetch; tile_data[2] = Core.VRAM[bus_address]; + //bus_address = 0x1800 + (LCDC.Bit(3) ? 1 : 0) * 0x400 + temp_fetch; VRAM_sel = tile_data[2].Bit(3) ? 1 : 0; BG_V_flip = tile_data[2].Bit(6) & Core.GBC_compat; - + } + else + { read_case = 1; if (!pre_render) { tile_inc++; - } + } } break; case 1: // read from tile graphics (0) - if ((internal_cycle % 2) == 1) + if ((internal_cycle % 2) == 0) { + read_case_prev = 1; + y_scroll_offset = (scroll_y + LY) % 8; if (BG_V_flip) @@ -1038,16 +912,20 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk tile_byte -= 256; } bus_address = (VRAM_sel * 0x2000) + 0x1000 + tile_byte * 16 + y_scroll_offset * 2; - tile_data[0] = Core.VRAM[bus_address]; + tile_data[0] = Core.VRAM[bus_address]; } - + } + else + { read_case = 2; } break; case 2: // read from tile graphics (1) - if ((internal_cycle % 2) == 1) + if ((internal_cycle % 2) == 0) { + read_case_prev = 2; + y_scroll_offset = (scroll_y + LY) % 8; if (BG_V_flip) @@ -1075,9 +953,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } bus_address = (VRAM_sel * 0x2000) + 0x1000 + tile_byte * 16 + y_scroll_offset * 2 + 1; - tile_data[1] = Core.VRAM[bus_address]; - } - + tile_data[1] = Core.VRAM[bus_address]; + } + } + else + { if (pre_render) { // here we set up rendering @@ -1105,7 +985,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk break; case 3: // read from tile data - if ((internal_cycle % 2) == 1) + if ((internal_cycle % 2) == 0) + { + read_case_prev = 3; + // What's on the bus? + } + else { read_case = 0; latch_new_data = true; @@ -1113,8 +998,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk break; case 4: // read from window data - if ((window_counter % 2) == 1) + if ((window_counter % 2) == 0) { + read_case_prev = 4; + temp_fetch = window_y_tile * 32 + (window_x_tile + window_tile_inc) % 32; tile_byte = Core.VRAM[0x1800 + (LCDC.Bit(6) ? 1 : 0) * 0x400 + temp_fetch]; @@ -1124,15 +1011,20 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk VRAM_sel = tile_data[2].Bit(3) ? 1 : 0; BG_V_flip = tile_data[2].Bit(6) & Core.GBC_compat; - window_tile_inc++; + window_tile_inc++; + } + else + { read_case = 5; } window_counter++; break; case 5: // read from tile graphics (for the window) - if ((window_counter % 2) == 1) + if ((window_counter % 2) == 0) { + read_case_prev = 5; + y_scroll_offset = window_y_tile_inc % 8; if (BG_V_flip) @@ -1155,16 +1047,20 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk bus_address = (VRAM_sel * 0x2000) + 0x1000 + tile_byte * 16 + y_scroll_offset * 2; tile_data[0] = Core.VRAM[bus_address]; - } - + } + } + else + { read_case = 6; } window_counter++; break; case 6: // read from tile graphics (for the window) - if ((window_counter % 2) == 1) + if ((window_counter % 2) == 0) { + read_case_prev = 6; + y_scroll_offset = window_y_tile_inc % 8; if (BG_V_flip) @@ -1194,7 +1090,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk bus_address = (VRAM_sel * 0x2000) + 0x1000 + tile_byte * 16 + y_scroll_offset * 2 + 1; tile_data[1] = Core.VRAM[bus_address]; } - + } + else + { if (window_pre_render) { // here we set up rendering @@ -1242,49 +1140,29 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk break; case 7: // read from tile data (window) - if ((window_counter % 2) == 1) + if ((window_counter % 2) == 0) + { + read_case_prev = 7; + // What's on the bus? + } + else { read_case = 4; latch_new_data = true; } - window_counter++; + window_counter++; break; case 8: // done reading, we are now in phase 0 pre_render = true; + was_pre_render = true; - if (hbl_countdown > 0) - { - hbl_countdown--; - if (hbl_countdown == 0) - { - OAM_access_read = true; - OAM_access_write = true; - VRAM_access_read = true; - VRAM_access_write = true; + OAM_access_read = true; + OAM_access_write = true; + VRAM_access_read = true; + VRAM_access_write = true; - read_case = 18; - } - else - { - STAT &= 0xFC; - STAT |= 0x00; - - if (STAT.Bit(3)) { HBL_INT = true; } - - // TODO: If Window is turned on midscanline what happens? When is this check done exactly? - if ((window_started && window_latch) || (window_is_reset && !window_latch && (LY > window_y_latch))) - { - window_y_tile_inc++; - if (window_y_tile_inc == 8) - { - window_y_tile_inc = 0; - window_y_tile++; - window_y_tile %= 32; - } - } - } - } + read_case = 18; break; case 9: @@ -1315,6 +1193,158 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk HDMA_can_start = true; break; } + + if (!was_pre_render) + { + // start shifting data into the LCD + if (render_counter >= (render_offset + 8)) + { + if (tile_data_latch[2].Bit(5) && Core.GBC_compat) + { + pixel = tile_data_latch[0].Bit(render_counter % 8) ? 1 : 0; + pixel |= tile_data_latch[1].Bit(render_counter % 8) ? 2 : 0; + } + else + { + pixel = tile_data_latch[0].Bit(7 - (render_counter % 8)) ? 1 : 0; + pixel |= tile_data_latch[1].Bit(7 - (render_counter % 8)) ? 2 : 0; + } + + int ref_pixel = pixel; + + if (!Core.GBC_compat) + { + if (LCDC.Bit(0)) + { + pixel = (BGP >> (pixel * 2)) & 3; + } + else + { + pixel = BGP & 3; + } + } + + int pal_num = tile_data_latch[2] & 0x7; + + bool use_sprite = false; + + int s_pixel = 0; + + // now we have the BG pixel, we next need the sprite pixel + if (!no_sprites) + { + bool have_sprite = false; + int sprite_attr = 0; + + if (sprite_present_list[pixel_counter] == 1) + { + have_sprite = true; + s_pixel = sprite_pixel_list[pixel_counter]; + sprite_attr = sprite_attr_list[pixel_counter]; + } + + if (have_sprite) + { + if (LCDC.Bit(1)) + { + if (!sprite_attr.Bit(7)) + { + use_sprite = true; + } + else if (ref_pixel == 0) + { + use_sprite = true; + } + + if (!LCDC.Bit(0)) + { + use_sprite = true; + } + + // There is another priority bit in GBC, that can still override sprite priority + if (LCDC.Bit(0) && tile_data_latch[2].Bit(7) && (ref_pixel != 0) && Core.GBC_compat) + { + use_sprite = false; + } + } + + if (use_sprite) + { + pal_num = sprite_attr & 7; + + if (!Core.GBC_compat) + { + pal_num = sprite_attr.Bit(4) ? 1 : 0; + + if (sprite_attr.Bit(4)) + { + pixel = (obj_pal_1 >> (s_pixel * 2)) & 3; + } + else + { + pixel = (obj_pal_0 >> (s_pixel * 2)) & 3; + } + } + } + } + } + + // based on sprite priority and pixel values, pick a final pixel color + if (Core.GBC_compat) + { + if (use_sprite) + { + Core.vid_buffer[LY * 160 + pixel_counter] = OBJ_palette[pal_num * 4 + s_pixel]; + } + else + { + Core.vid_buffer[LY * 160 + pixel_counter] = BG_palette[pal_num * 4 + pixel]; + } + } + else + { + if (use_sprite) + { + Core.vid_buffer[LY * 160 + pixel_counter] = OBJ_palette[pal_num * 4 + pixel]; + } + else + { + Core.vid_buffer[LY * 160 + pixel_counter] = BG_palette[pixel]; + } + } + + pixel_counter++; + + if (pixel_counter == 160) + { + read_case = 8; + // hbl_countdown = 1; + + STAT &= 0xFC; + STAT |= 0x00; + + if (STAT.Bit(3)) { HBL_INT = true; } + + // TODO: If Window is turned on midscanline what happens? When is this check done exactly? + if ((window_started && window_latch) || (window_is_reset && !window_latch && (LY > window_y_latch))) + { + window_y_tile_inc++; + if (window_y_tile_inc == 8) + { + window_y_tile_inc = 0; + window_y_tile++; + window_y_tile %= 32; + } + } + } + } + else if (pixel_counter < 0) + { + pixel_counter++; + } + render_counter++; + } + internal_cycle++; if (latch_new_data) @@ -1324,6 +1354,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk tile_data_latch[1] = tile_data[1]; tile_data_latch[2] = tile_data[2]; } + + was_pre_render = pre_render; } // every in range sprite takes 6 cycles to process @@ -1797,6 +1829,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk for (int i = 0; i < BG_bytes.Length; i++) { BG_bytes[i] = 0xFF; } for (int i = 0; i < OBJ_bytes.Length; i++) { OBJ_bytes[i] = 0xFF; } + + pal_change_blocked = false; } } } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs index b1ce6001b8..f9f375d260 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs @@ -759,6 +759,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk read_case = 0; internal_cycle = 0; pre_render = true; + was_pre_render = true; pre_render_2 = true; tile_inc = 0; pixel_counter = -8; @@ -830,6 +831,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // don't evaluate sprites until pre-render for window is over pre_render = true; + was_pre_render = true; pre_render_2 = true; } @@ -848,107 +850,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } } - if (!pre_render && !fetch_sprite) - { - // start shifting data into the LCD - if (render_counter >= (render_offset + 8)) - { - if (tile_data_latch[2].Bit(5)) - { - pixel = tile_data_latch[0].Bit(render_counter % 8) ? 1 : 0; - pixel |= tile_data_latch[1].Bit(render_counter % 8) ? 2 : 0; - } - else - { - pixel = tile_data_latch[0].Bit(7 - (render_counter % 8)) ? 1 : 0; - pixel |= tile_data_latch[1].Bit(7 - (render_counter % 8)) ? 2 : 0; - } - - int ref_pixel = pixel; - - int pal_num = tile_data_latch[2] & 0x7; - - bool use_sprite = false; - - int s_pixel = 0; - - // now we have the BG pixel, we next need the sprite pixel - if (!no_sprites) - { - bool have_sprite = false; - int sprite_attr = 0; - - if (sprite_present_list[pixel_counter] == 1) - { - have_sprite = true; - s_pixel = sprite_pixel_list[pixel_counter]; - sprite_attr = sprite_attr_list[pixel_counter]; - } - - if (have_sprite) - { - if (LCDC.Bit(1)) - { - if (!sprite_attr.Bit(7)) - { - use_sprite = true; - } - else if (ref_pixel == 0) - { - use_sprite = true; - } - - if (!LCDC.Bit(0)) - { - use_sprite = true; - } - - // There is another priority bit in GBC, that can still override sprite priority - if (LCDC.Bit(0) && tile_data_latch[2].Bit(7) && (ref_pixel != 0)) - { - use_sprite = false; - } - } - - if (use_sprite) - { - pal_num = sprite_attr & 7; - } - } - } - - // based on sprite priority and pixel values, pick a final pixel color - if (use_sprite) - { - Core.vid_buffer[LY * 160 + pixel_counter] = OBJ_palette[pal_num * 4 + s_pixel]; - } - else - { - Core.vid_buffer[LY * 160 + pixel_counter] = BG_palette[pal_num * 4 + pixel]; - } - - pixel_counter++; - - if (pixel_counter == 160) - { - read_case = 8; - hbl_countdown = 2; - } - } - else if (pixel_counter < 0) - { - pixel_counter++; - } - render_counter++; - } - if (!fetch_sprite) - { + { switch (read_case) { case 0: // read a background tile - if ((internal_cycle % 2) == 1) + if ((internal_cycle % 2) == 0) { + read_case_prev = 0; + // calculate the row number of the tiles to be fetched y_tile = (((int)scroll_y + LY) >> 3) % 32; x_tile = scroll_x >> 3; @@ -962,18 +872,22 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk VRAM_sel = tile_data[2].Bit(3) ? 1 : 0; BG_V_flip = tile_data[2].Bit(6); - + } + else + { read_case = 1; if (!pre_render) { tile_inc++; - } + } } break; case 1: // read from tile graphics (0) - if ((internal_cycle % 2) == 1) + if ((internal_cycle % 2) == 0) { + read_case_prev = 1; + y_scroll_offset = (scroll_y + LY) % 8; if (BG_V_flip) @@ -997,14 +911,18 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk bus_address = (VRAM_sel * 0x2000) + 0x1000 + tile_byte * 16 + y_scroll_offset * 2; tile_data[0] = Core.VRAM[bus_address]; } - + } + else + { read_case = 2; } break; case 2: // read from tile graphics (1) - if ((internal_cycle % 2) == 1) + if ((internal_cycle % 2) == 0) { + read_case_prev = 2; + y_scroll_offset = (scroll_y + LY) % 8; if (BG_V_flip) @@ -1034,7 +952,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk bus_address = (VRAM_sel * 0x2000) + 0x1000 + tile_byte * 16 + y_scroll_offset * 2 + 1; tile_data[1] = Core.VRAM[bus_address]; } - + } + else + { if (pre_render) { // here we set up rendering @@ -1062,7 +982,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk break; case 3: // read from tile data - if ((internal_cycle % 2) == 1) + if ((internal_cycle % 2) == 0) + { + read_case_prev = 3; + // What's on the bus? + } + else { read_case = 0; latch_new_data = true; @@ -1070,8 +995,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk break; case 4: // read from window data - if ((window_counter % 2) == 1) - { + if ((window_counter % 2) == 0) + { + read_case_prev = 4; + temp_fetch = window_y_tile * 32 + (window_x_tile + window_tile_inc) % 32; tile_byte = Core.VRAM[0x1800 + (LCDC.Bit(6) ? 1 : 0) * 0x400 + temp_fetch]; @@ -1082,14 +1009,19 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk BG_V_flip = tile_data[2].Bit(6); window_tile_inc++; + } + else + { read_case = 5; } window_counter++; break; case 5: // read from tile graphics (for the window) - if ((window_counter % 2) == 1) + if ((window_counter % 2) == 0) { + read_case_prev = 5; + y_scroll_offset = window_y_tile_inc % 8; if (BG_V_flip) @@ -1113,15 +1045,19 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk bus_address = (VRAM_sel * 0x2000) + 0x1000 + tile_byte * 16 + y_scroll_offset * 2; tile_data[0] = Core.VRAM[bus_address]; } - + } + else + { read_case = 6; } window_counter++; break; case 6: // read from tile graphics (for the window) - if ((window_counter % 2) == 1) + if ((window_counter % 2) == 0) { + read_case_prev = 6; + y_scroll_offset = window_y_tile_inc % 8; if (BG_V_flip) @@ -1151,7 +1087,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk bus_address = (VRAM_sel * 0x2000) + 0x1000 + tile_byte * 16 + y_scroll_offset * 2 + 1; tile_data[1] = Core.VRAM[bus_address]; } - + } + else + { if (window_pre_render) { // here we set up rendering @@ -1188,7 +1126,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk latch_counter = 0; latch_new_data = true; - window_pre_render = false; + window_pre_render = false; } else { @@ -1199,59 +1137,35 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk break; case 7: // read from tile data (window) - if ((window_counter % 2) == 1) + if ((window_counter % 2) == 0) + { + read_case_prev = 7; + // What's on the bus? + } + else { read_case = 4; latch_new_data = true; } - window_counter++; + window_counter++; break; case 8: // done reading, we are now in phase 0 pre_render = true; + was_pre_render = true; - if (hbl_countdown > 0) + OAM_access_read = true; + OAM_access_write = true; + VRAM_access_read = true; + VRAM_access_write = true; + read_case = 18; + + if (Core.double_speed) { - hbl_countdown--; - - if (hbl_countdown == 0) - { - OAM_access_read = true; - OAM_access_write = true; - VRAM_access_read = true; - VRAM_access_write = true; - read_case = 18; - - if (Core.double_speed) - { - STAT &= 0xFC; - STAT |= 0x00; - if (STAT.Bit(3)) { HBL_INT = true; } - // the CPU has to be able to see the transition from mode 3 to mode 0 to start HDMA - } - } - else - { - if (!Core.double_speed) - { - STAT &= 0xFC; - STAT |= 0x00; - if (STAT.Bit(3)) { HBL_INT = true; } - // the CPU has to be able to see the transition from mode 3 to mode 0 to start HDMA - } - - // TODO: If Window is turned on midscanline what happens? When is this check done exactly? - if ((window_started && window_latch) || (window_is_reset && !window_latch && (LY > window_y_latch))) - { - window_y_tile_inc++; - if (window_y_tile_inc == 8) - { - window_y_tile_inc = 0; - window_y_tile++; - window_y_tile %= 32; - } - } - } + STAT &= 0xFC; + STAT |= 0x00; + if (STAT.Bit(3)) { HBL_INT = true; } + // the CPU has to be able to see the transition from mode 3 to mode 0 to start HDMA } break; @@ -1275,7 +1189,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk case 18: case 19: case 20: - read_case++; + read_case++; break; case 21: // hardware tests indicate that HDMA starts at this point, not immediately after mode 3 ends @@ -1283,6 +1197,121 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk HDMA_can_start = true; break; } + + if (!was_pre_render) + { + // start shifting data into the LCD + if (render_counter >= (render_offset + 8)) + { + if (tile_data_latch[2].Bit(5)) + { + pixel = tile_data_latch[0].Bit(render_counter % 8) ? 1 : 0; + pixel |= tile_data_latch[1].Bit(render_counter % 8) ? 2 : 0; + } + else + { + pixel = tile_data_latch[0].Bit(7 - (render_counter % 8)) ? 1 : 0; + pixel |= tile_data_latch[1].Bit(7 - (render_counter % 8)) ? 2 : 0; + } + + int ref_pixel = pixel; + + int pal_num = tile_data_latch[2] & 0x7; + + bool use_sprite = false; + + int s_pixel = 0; + + // now we have the BG pixel, we next need the sprite pixel + if (!no_sprites) + { + bool have_sprite = false; + int sprite_attr = 0; + + if (sprite_present_list[pixel_counter] == 1) + { + have_sprite = true; + s_pixel = sprite_pixel_list[pixel_counter]; + sprite_attr = sprite_attr_list[pixel_counter]; + } + + if (have_sprite) + { + if (LCDC.Bit(1)) + { + if (!sprite_attr.Bit(7)) + { + use_sprite = true; + } + else if (ref_pixel == 0) + { + use_sprite = true; + } + + if (!LCDC.Bit(0)) + { + use_sprite = true; + } + + // There is another priority bit in GBC, that can still override sprite priority + if (LCDC.Bit(0) && tile_data_latch[2].Bit(7) && (ref_pixel != 0)) + { + use_sprite = false; + } + } + + if (use_sprite) + { + pal_num = sprite_attr & 7; + } + } + } + + // based on sprite priority and pixel values, pick a final pixel color + if (use_sprite) + { + Core.vid_buffer[LY * 160 + pixel_counter] = OBJ_palette[pal_num * 4 + s_pixel]; + } + else + { + Core.vid_buffer[LY * 160 + pixel_counter] = BG_palette[pal_num * 4 + pixel]; + } + + pixel_counter++; + + if (pixel_counter == 160) + { + read_case = 8; + // hbl_countdown = 1; + + if (!Core.double_speed) + { + STAT &= 0xFC; + STAT |= 0x00; + if (STAT.Bit(3)) { HBL_INT = true; } + // the CPU has to be able to see the transition from mode 3 to mode 0 to start HDMA + } + + // TODO: If Window is turned on midscanline what happens? When is this check done exactly? + if ((window_started && window_latch) || (window_is_reset && !window_latch && (LY > window_y_latch))) + { + window_y_tile_inc++; + if (window_y_tile_inc == 8) + { + window_y_tile_inc = 0; + window_y_tile++; + window_y_tile %= 32; + } + } + } + } + else if (pixel_counter < 0) + { + pixel_counter++; + } + render_counter++; + } + internal_cycle++; if (latch_new_data) @@ -1292,6 +1321,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk tile_data_latch[1] = tile_data[1]; tile_data_latch[2] = tile_data[2]; } + + was_pre_render = pre_render; } // every in range sprite takes 6 cycles to process diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs index 9447769e78..ce2a0b0ac5 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs @@ -449,6 +449,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk read_case = 0; internal_cycle = 0; pre_render = true; + was_pre_render = true; pre_render_2 = true; tile_inc = 0; pixel_counter = -8; @@ -517,6 +518,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // don't evaluate sprites until pre-render for window is over pre_render = true; + was_pre_render = true; pre_render_2 = true; } @@ -536,96 +538,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } } - if (!pre_render && !fetch_sprite) - { - // start shifting data into the LCD - if (render_counter >= (render_offset + 8)) - { - pixel = tile_data_latch[0].Bit(7 - (render_counter % 8)) ? 1 : 0; - pixel |= tile_data_latch[1].Bit(7 - (render_counter % 8)) ? 2 : 0; - - int ref_pixel = pixel; - if (LCDC.Bit(0)) - { - pixel = (BGP >> (pixel * 2)) & 3; - } - else - { - pixel = BGP & 3; - } - - // now we have the BG pixel, we next need the sprite pixel - if (!no_sprites) - { - bool have_sprite = false; - int s_pixel = 0; - int sprite_attr = 0; - - if (sprite_present_list[pixel_counter] == 1) - { - have_sprite = true; - s_pixel = sprite_pixel_list[pixel_counter]; - sprite_attr = sprite_attr_list[pixel_counter]; - } - - if (have_sprite) - { - bool use_sprite = false; - if (LCDC.Bit(1)) - { - if (!sprite_attr.Bit(7)) - { - use_sprite = true; - } - else if (ref_pixel == 0) - { - use_sprite = true; - } - - if (!LCDC.Bit(0)) - { - use_sprite = true; - } - } - - if (use_sprite) - { - if (sprite_attr.Bit(4)) - { - pixel = (obj_pal_1 >> (s_pixel * 2)) & 3; - } - else - { - pixel = (obj_pal_0 >> (s_pixel * 2)) & 3; - } - } - } - } - - // based on sprite priority and pixel values, pick a final pixel color - Core.vid_buffer[LY * 160 + pixel_counter] = color_palette[pixel]; - pixel_counter++; - - if (pixel_counter == 160) - { - hbl_countdown = 2; - read_case = 8; - } - } - else if (pixel_counter < 0) - { - pixel_counter++; - } - render_counter++; - } - if (!fetch_sprite) { switch (read_case) { case 0: // read a background tile - if ((internal_cycle % 2) == 1) + if ((internal_cycle % 2) == 0) { + read_case_prev = 0; + // calculate the row number of the tiles to be fetched y_tile = (((int)scroll_y + LY) >> 3) % 32; x_tile = scroll_x >> 3; @@ -634,19 +555,23 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk bus_address = 0x1800 + (LCDC.Bit(3) ? 1 : 0) * 0x400 + temp_fetch; tile_byte = Core.VRAM[bus_address]; - + } + else + { read_case = 1; if (!pre_render) { tile_inc++; - } + } } break; case 1: // read from tile graphics (0) - if ((internal_cycle % 2) == 1) + if ((internal_cycle % 2) == 0) { + read_case_prev = 1; + y_scroll_offset = (scroll_y + LY) % 8; if (LCDC.Bit(4)) @@ -665,14 +590,18 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk bus_address = 0x1000 + tile_byte * 16 + y_scroll_offset * 2; tile_data[0] = Core.VRAM[bus_address]; } - + } + else + { read_case = 2; } break; case 2: // read from tile graphics (1) - if ((internal_cycle % 2) == 1) - { + if ((internal_cycle % 2) == 0) + { + read_case_prev = 2; + y_scroll_offset = (scroll_y + LY) % 8; if (LCDC.Bit(4)) @@ -697,7 +626,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk bus_address = 0x1000 + tile_byte * 16 + y_scroll_offset * 2 + 1; tile_data[1] = Core.VRAM[bus_address]; } - + } + else + { if (pre_render) { // here we set up rendering @@ -725,30 +656,42 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk break; case 3: // read from tile data - if ((internal_cycle % 2) == 1) - { + if ((internal_cycle % 2) == 0) + { + read_case_prev = 3; + // What's on the bus? + } + else + { read_case = 0; latch_new_data = true; } break; case 4: // read from window data - if ((window_counter % 2) == 1) + if ((window_counter % 2) == 0) { + read_case_prev = 4; + temp_fetch = window_y_tile * 32 + (window_x_tile + window_tile_inc) % 32; bus_address = 0x1800 + (LCDC.Bit(6) ? 1 : 0) * 0x400 + temp_fetch; tile_byte = Core.VRAM[bus_address]; window_tile_inc++; + } + else + { read_case = 5; } window_counter++; break; case 5: // read from tile graphics (for the window) - if ((window_counter % 2) == 1) + if ((window_counter % 2) == 0) { + read_case_prev = 5; + y_scroll_offset = (window_y_tile_inc) % 8; if (LCDC.Bit(4)) @@ -767,15 +710,19 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk bus_address = 0x1000 + tile_byte * 16 + y_scroll_offset * 2; tile_data[0] = Core.VRAM[bus_address]; } - + } + else + { read_case = 6; } window_counter++; break; case 6: // read from tile graphics (for the window) - if ((window_counter % 2) == 1) + if ((window_counter % 2) == 0) { + read_case_prev = 6; + y_scroll_offset = (window_y_tile_inc) % 8; if (LCDC.Bit(4)) { @@ -799,7 +746,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk bus_address = 0x1000 + tile_byte * 16 + y_scroll_offset * 2 + 1; tile_data[1] = Core.VRAM[bus_address]; } - + } + else + { if (window_pre_render) { // here we set up rendering @@ -847,49 +796,29 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk break; case 7: // read from tile data (window) - if ((window_counter % 2) == 1) + if ((window_counter % 2) == 0) + { + read_case_prev = 7; + // What's on the bus? + } + else { read_case = 4; latch_new_data = true; } - window_counter++; + window_counter++; break; case 8: // done reading, we are now in phase 0 pre_render = true; + was_pre_render = true; - if (hbl_countdown > 0) - { - hbl_countdown--; - if (hbl_countdown == 0) - { - VRAM_access_read = true; - VRAM_access_write = true; - OAM_access_read = true; - OAM_access_write = true; + VRAM_access_read = true; + VRAM_access_write = true; + OAM_access_read = true; + OAM_access_write = true; - read_case = 18; - } - else - { - STAT &= 0xFC; - STAT |= 0x00; - - if (STAT.Bit(3)) { HBL_INT = true; } - - // TODO: If Window is turned on midscanline what happens? When is this check done exactly? - if ((window_started && window_latch) || (window_is_reset && !window_latch && (LY > window_y_latch))) - { - window_y_tile_inc++; - if (window_y_tile_inc == 8) - { - window_y_tile_inc = 0; - window_y_tile++; - window_y_tile %= 32; - } - } - } - } + read_case = 18; break; case 9: @@ -913,6 +842,107 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk rendering_complete = true; break; } + + if (!was_pre_render) + { + // start shifting data into the LCD + if (render_counter >= (render_offset + 8)) + { + pixel = tile_data_latch[0].Bit(7 - (render_counter % 8)) ? 1 : 0; + pixel |= tile_data_latch[1].Bit(7 - (render_counter % 8)) ? 2 : 0; + + int ref_pixel = pixel; + if (LCDC.Bit(0)) + { + pixel = (BGP >> (pixel * 2)) & 3; + } + else + { + pixel = BGP & 3; + } + + // now we have the BG pixel, we next need the sprite pixel + if (!no_sprites) + { + bool have_sprite = false; + int s_pixel = 0; + int sprite_attr = 0; + + if (sprite_present_list[pixel_counter] == 1) + { + have_sprite = true; + s_pixel = sprite_pixel_list[pixel_counter]; + sprite_attr = sprite_attr_list[pixel_counter]; + } + + if (have_sprite) + { + bool use_sprite = false; + if (LCDC.Bit(1)) + { + if (!sprite_attr.Bit(7)) + { + use_sprite = true; + } + else if (ref_pixel == 0) + { + use_sprite = true; + } + + if (!LCDC.Bit(0)) + { + use_sprite = true; + } + } + + if (use_sprite) + { + if (sprite_attr.Bit(4)) + { + pixel = (obj_pal_1 >> (s_pixel * 2)) & 3; + } + else + { + pixel = (obj_pal_0 >> (s_pixel * 2)) & 3; + } + } + } + } + + // based on sprite priority and pixel values, pick a final pixel color + Core.vid_buffer[LY * 160 + pixel_counter] = color_palette[pixel]; + pixel_counter++; + + if (pixel_counter == 160) + { + read_case = 8; + // hbl_countdown = 1; + + STAT &= 0xFC; + STAT |= 0x00; + + if (STAT.Bit(3)) { HBL_INT = true; } + + // TODO: If Window is turned on midscanline what happens? When is this check done exactly? + if ((window_started && window_latch) || (window_is_reset && !window_latch && (LY > window_y_latch))) + { + window_y_tile_inc++; + if (window_y_tile_inc == 8) + { + window_y_tile_inc = 0; + window_y_tile++; + window_y_tile %= 32; + } + } + } + } + else if (pixel_counter < 0) + { + pixel_counter++; + } + render_counter++; + } + internal_cycle++; if (latch_new_data) @@ -921,6 +951,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk tile_data_latch[0] = tile_data[0]; tile_data_latch[1] = tile_data[1]; } + + was_pre_render = pre_render; } // every in range sprite takes 6 cycles to process diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/HW_Registers.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/HW_Registers.cs index 3bfa0b06cd..935416d907 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/HW_Registers.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/HW_Registers.cs @@ -407,6 +407,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (GB_bios_register == 0) { GB_bios_register = value; + if (!GBC_compat) { ppu.pal_change_blocked = true; } } break; @@ -502,7 +503,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk break; default: - Console.WriteLine(addr + " " + value); + //Console.WriteLine(addr + " " + value); break; } } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC1_Multi.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC1_Multi.cs index d6acccd6ac..e306c13689 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC1_Multi.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC1_Multi.cs @@ -64,7 +64,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } else { - return 0; + return 0xFF; } } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC3.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC3.cs index 07036610cc..2152062e30 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC3.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC3.cs @@ -81,12 +81,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } else { - return 0x0; + return 0xFF; } } else { - return 0x0; + return 0xFF; } } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/MemoryMap.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/MemoryMap.cs index 8f6c62d925..a9ddd5d465 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/MemoryMap.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/MemoryMap.cs @@ -143,9 +143,27 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { if (ppu.pixel_counter == 160) { + //Console.WriteLine("VRAM Glitch " + cpu.TotalExecutedCycles + " " + ppu.bus_address + " " + + //VRAM[ppu.bus_address] + " " + ppu.read_case_prev + " " + (ppu.internal_cycle & 1) + " " + + //(VRAM_Bank * 0x2000 + (addr - 0x8000)) + " " + VRAM[VRAM_Bank * 0x2000 + (addr - 0x8000)]); + + // This is a complicated case because the PPU is accessing 2 areas of VRAM at the same time. + if (is_GBC && ((ppu.read_case_prev == 0) || (ppu.read_case_prev == 4))) + { + //if ((VRAM_Bank * 0x2000 + (addr - 0x8000)) < 0x3800) + //{ + // return VRAM[VRAM_Bank * 0x2000 + (addr - 0x8000)]; + //} + return VRAM[ppu.bus_address]; + } + + // What is returned when the ppu isn't accessing VRAM? + if ((ppu.read_case_prev == 3) || (ppu.read_case_prev == 7)) + { + return VRAM[VRAM_Bank * 0x2000 + (addr - 0x8000)]; + } return VRAM[ppu.bus_address]; } - return 0xFF; } else diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs index ce8e1efb86..7eb442e0c5 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs @@ -110,6 +110,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public int hbl_countdown; public int sprite_scroll_offset; + public bool was_pre_render; + public int read_case_prev; + public bool pal_change_blocked; // in compatability mode, you can change palette values but not displayed color // variables not in state public int total_counter; @@ -270,6 +273,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ser.Sync(nameof(hbl_countdown), ref hbl_countdown); ser.Sync(nameof(sprite_scroll_offset), ref sprite_scroll_offset); + ser.Sync(nameof(was_pre_render), ref was_pre_render); + ser.Sync(nameof(read_case_prev), ref read_case_prev); + ser.Sync(nameof(pal_change_blocked), ref pal_change_blocked); } } }