GBHawk: refactor some ppu code to prepare for more accurate VRAm glitch emulation

This commit is contained in:
alyosha-tas 2020-10-29 12:35:45 -04:00
parent bd3ee6f45c
commit 2a92121483
8 changed files with 602 additions and 480 deletions

View File

@ -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;
}
}
}

View File

@ -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

View File

@ -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

View File

@ -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;
}
}

View File

@ -64,7 +64,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
}
else
{
return 0;
return 0xFF;
}
}

View File

@ -81,12 +81,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
}
else
{
return 0x0;
return 0xFF;
}
}
else
{
return 0x0;
return 0xFF;
}
}

View File

@ -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

View File

@ -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);
}
}
}