GBHawk: Sprite Evaluation work

This commit is contained in:
alyosha-tas 2018-01-17 21:38:00 -05:00
parent 5d3ca7bc47
commit 5bf7b060af
1 changed files with 260 additions and 163 deletions

View File

@ -59,6 +59,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
public int tile_byte;
public int sprite_fetch_cycles;
public bool fetch_sprite;
public bool fetch_sprite_01;
public bool going_to_fetch;
public int sprite_fetch_counter;
public bool glitchy_eval;
public byte[] sprite_attr_list = new byte[160];
public byte[] sprite_pixel_list = new byte[160];
public byte[] sprite_present_list = new byte[160];
public int temp_fetch;
public int tile_inc;
public bool pre_render;
@ -74,11 +81,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
public byte[] sprite_sel = new byte[2];
public int sl_use_index;
public bool no_sprites;
public int sprite_fetch_index;
public int[] SL_sprites_ordered = new int[40]; // (x_end, data_low, data_high, attr)
public int index_used;
public int evaled_sprites;
public int sprite_ordered_index;
public int bottom_index;
public bool blank_frame;
// windowing state
@ -582,11 +587,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
tile_inc = 0;
pixel_counter = 0;
sl_use_index = 0;
index_used = 0;
bottom_index = 0;
sprite_ordered_index = 0;
fetch_sprite = false;
fetch_sprite_01 = false;
going_to_fetch = false;
no_sprites = false;
glitchy_eval = false;
evaled_sprites = 0;
window_pre_render = false;
if (window_started && LCDC.Bit(5))
@ -601,12 +607,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
}
window_started = false;
if (SL_sprites_index == 0)
{
no_sprites = true;
}
if (SL_sprites_index == 0) { no_sprites = true; }
// it is much easier to process sprites if we order them according to the rules of sprite priority first
if (!no_sprites) { reorder_and_assemble_sprites(); }
}
// before anything else, we have to check if windowing is in effect
if (LCDC.Bit(5) && !window_started && (LY >= window_y) && (pixel_counter >= (window_x_latch - 7)) && (window_x_latch < 167))
{
@ -643,132 +649,109 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
if (!pre_render && !fetch_sprite && !window_pre_render)
{
// start by fetching all the sprites that need to be fetched
if (!no_sprites)
// start shifting data into the LCD
if (render_counter >= (render_offset + 8))
{
for (int i = 0; i < SL_sprites_index; i++)
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))
{
if ((pixel_counter >= (SL_sprites[i * 4 + 1] - 8)) &&
(pixel_counter < SL_sprites[i * 4 + 1]) &&
!index_used.Bit(i))
{
fetch_sprite = true;
sprite_fetch_index = 0;
}
pixel = (BGP >> (pixel * 2)) & 3;
}
}
if (!fetch_sprite)
{
// start shifting data into the LCD
if (render_counter >= (render_offset + 8))
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 (LCDC.Bit(0))
{
pixel = (BGP >> (pixel * 2)) & 3;
}
else
{
pixel = 0;
}
pixel = 0;
}
// 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;
// now we have the BG pixel, we next need the sprite pixel
if (!no_sprites)
if (sprite_present_list[pixel_counter] == 1)
{
bool have_sprite = false;
int i = bottom_index;
int s_pixel = 0;
int sprite_attr = 0;
while (i < sprite_ordered_index)
{
if (SL_sprites_ordered[i * 4] == pixel_counter)
{
bottom_index++;
if (bottom_index == SL_sprites_index) { no_sprites = true; }
}
else if (!have_sprite)
{
// we can use the current sprite, so pick out a pixel for it
int t_index = pixel_counter - (SL_sprites_ordered[i * 4] - 8);
t_index = 7 - t_index;
sprite_data[0] = (byte)((SL_sprites_ordered[i * 4 + 1] >> t_index) & 1);
sprite_data[1] = (byte)(((SL_sprites_ordered[i * 4 + 2] >> t_index) & 1) << 1);
s_pixel = sprite_data[0] + sprite_data[1];
sprite_attr = SL_sprites_ordered[i * 4 + 3];
// pixel color of 0 is transparent, so if this is the case we dont have a pixel
if (s_pixel != 0)
{
have_sprite = true;
}
}
i++;
}
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;
}
}
}
have_sprite = true;
s_pixel = sprite_pixel_list[pixel_counter];
sprite_attr = sprite_attr_list[pixel_counter];
}
// based on sprite priority and pixel values, pick a final pixel color
Core._vidbuffer[LY * 160 + pixel_counter] = (int)Core.color_palette[pixel];
pixel_counter++;
if (pixel_counter == 160)
if (have_sprite)
{
read_case = 8;
hbl_countdown = 6;
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;
}
}
}
}
render_counter++;
// based on sprite priority and pixel values, pick a final pixel color
Core._vidbuffer[LY * 160 + pixel_counter] = (int)Core.color_palette[pixel];
pixel_counter++;
if (pixel_counter == 160)
{
read_case = 8;
hbl_countdown = 7;
}
}
render_counter++;
}
if (!fetch_sprite)
{
if (latch_new_data)
if (!pre_render)
{
latch_new_data = false;
tile_data_latch[0] = tile_data[0];
tile_data_latch[1] = tile_data[1];
// before we go on to read case 3, we need to know if we stall there or not
// Gekkio's tests show that if sprites are at position 0 or 1 (mod 8)
// then it takes an extra cycle (1 or 2 more t-states) to process them
if (!no_sprites && (pixel_counter < 160))
{
for (int i = 0; i < SL_sprites_index; i++)
{
if ((pixel_counter >= (SL_sprites[i * 4 + 1] - 8)) &&
(pixel_counter < (SL_sprites[i * 4 + 1])) &&
!evaled_sprites.Bit(i))
{
going_to_fetch = true;
fetch_sprite = true;
if ((SL_sprites[i * 4 + 1] % 8) < 2)
{
fetch_sprite_01 = true;
}
}
}
}
}
switch (read_case)
@ -856,15 +839,33 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
// here we set up rendering
pre_render = false;
render_offset = scroll_x % 8;
render_counter = -1;
render_counter = 0; // -1;
latch_counter = 0;
read_case = 0;
// here we also do a glitchy sprite evaluation for sprites with x=0
if (!no_sprites)
{
for (int i = 0; i < SL_sprites_index; i++)
{
if (SL_sprites[i * 4 + 1] == 0)
{
going_to_fetch = true;
fetch_sprite = true;
glitchy_eval = true;
if ((SL_sprites[i * 4 + 1] % 8) < 2)
{
fetch_sprite_01 = true;
}
}
}
}
}
else
{
read_case = 3;
}
}
break;
@ -959,13 +960,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
// here we set up rendering
window_pre_render = false;
render_offset = 0;
render_counter = -1;
render_counter = 0; // -1;
latch_counter = 0;
read_case = 4;
}
else
{
read_case = 7;
}
}
window_counter++;
@ -1009,54 +1011,69 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
break;
}
internal_cycle++;
if (latch_new_data)
{
latch_new_data = false;
tile_data_latch[0] = tile_data[0];
tile_data_latch[1] = tile_data[1];
}
}
// every in range sprite takes 6 cycles to process
// sprites located at x=0 still take 6 cycles to process even though they don't appear on screen
// sprites above x=168 do not take any cycles to process however
if (fetch_sprite)
{
if (sprite_fetch_index < SL_sprites_index)
if (going_to_fetch)
{
if (pixel_counter != 0) {
if ((pixel_counter == (SL_sprites[sprite_fetch_index * 4 + 1] - 8)) &&
//(pixel_counter < SL_sprites[sprite_fetch_index * 4 + 1]) &&
!index_used.Bit(sprite_fetch_index))
{
sl_use_index = sprite_fetch_index;
process_sprite();
SL_sprites_ordered[sprite_ordered_index * 4] = SL_sprites[sprite_fetch_index * 4 + 1];
SL_sprites_ordered[sprite_ordered_index * 4 + 1] = sprite_sel[0];
SL_sprites_ordered[sprite_ordered_index * 4 + 2] = sprite_sel[1];
SL_sprites_ordered[sprite_ordered_index * 4 + 3] = SL_sprites[sprite_fetch_index * 4 + 3];
sprite_ordered_index++;
index_used |= (1 << sl_use_index);
}
sprite_fetch_index++;
if (sprite_fetch_index == SL_sprites_index) { fetch_sprite = false; }
}
else
going_to_fetch = false;
sprite_fetch_counter = 0;
if (fetch_sprite_01)
{
// whan pixel counter is 0, we want to scan all the points before 0 as well
// certainly non-physical but good enough for now
for (int j = -7; j < 1; j++)
sprite_fetch_counter += 2;
fetch_sprite_01 = false;
}
// at this time it is unknown what each cycle does, but we only need to accurately keep track of cycles
for (int i = 0; i < SL_sprites_index; i++)
{
if (glitchy_eval && (SL_sprites[i * 4 + 1] == 0))
{
for (int i = 0; i < SL_sprites_index; i++)
{
if ((j == (SL_sprites[i * 4 + 1] - 8)) &&
!index_used.Bit(i))
{
sl_use_index = i;
process_sprite();
SL_sprites_ordered[sprite_ordered_index * 4] = SL_sprites[i * 4 + 1];
SL_sprites_ordered[sprite_ordered_index * 4 + 1] = sprite_sel[0];
SL_sprites_ordered[sprite_ordered_index * 4 + 2] = sprite_sel[1];
SL_sprites_ordered[sprite_ordered_index * 4 + 3] = SL_sprites[i * 4 + 3];
sprite_ordered_index++;
index_used |= (1 << sl_use_index);
}
}
sprite_fetch_counter += 6;
evaled_sprites |= (1 << i);
}
else if ((pixel_counter >= (SL_sprites[i * 4 + 1] - 8)) &&
(pixel_counter < (SL_sprites[i * 4 + 1])) &&
!evaled_sprites.Bit(i))
{
sprite_fetch_counter += 6;
evaled_sprites |= (1 << i);
//Console.Write(SL_sprites[i * 4 + 1]);
//Console.Write(" ");
}
}
// if we didn't evaluate all the sprites immediately, 2 more cycles are added to restart it
if (evaled_sprites != (Math.Pow(2,SL_sprites_index) - 1))
{
sprite_fetch_counter += 2;
}
glitchy_eval = false;
//Console.WriteLine(sprite_fetch_counter);
}
else
{
sprite_fetch_counter--;
if (sprite_fetch_counter == 0)
{
fetch_sprite = false;
}
}
}
}
}
@ -1081,6 +1098,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
VRAM_access_read = true;
OAM_access_write = true;
VRAM_access_write = true;
DMA_OAM_access = true;
cycle = 0;
LYC_INT = false;
@ -1156,6 +1174,81 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
}
}
// order sprites according to x coordinate
// note that for sprites of equal x coordinate, priority goes to first on the list
public void reorder_and_assemble_sprites()
{
sprite_ordered_index = 0;
/*
for (int i = 0; i < SL_sprites_index; i++)
{
Console.Write(SL_sprites[i * 4 + 1]);
Console.Write(" ");
}
Console.WriteLine(" ");
*/
for (int i = 0; i < 256; i++)
{
for (int j = 0; j < SL_sprites_index; j++)
{
if (SL_sprites[j * 4 + 1] == i)
{
sl_use_index = j;
process_sprite();
SL_sprites_ordered[sprite_ordered_index * 4] = SL_sprites[j * 4 + 1];
SL_sprites_ordered[sprite_ordered_index * 4 + 1] = sprite_sel[0];
SL_sprites_ordered[sprite_ordered_index * 4 + 2] = sprite_sel[1];
SL_sprites_ordered[sprite_ordered_index * 4 + 3] = SL_sprites[j * 4 + 3];
sprite_ordered_index++;
}
}
}
bool have_pixel = false;
byte s_pixel = 0;
byte sprite_attr = 0;
for (int i = 0; i < 160; i++)
{
have_pixel = false;
for (int j = 0; j < SL_sprites_index; j++)
{
if ((i >= (SL_sprites_ordered[j * 4] - 8)) &&
(i < SL_sprites_ordered[j * 4]) &&
!have_pixel)
{
// we can use the current sprite, so pick out a pixel for it
int t_index = i - (SL_sprites_ordered[j * 4] - 8);
t_index = 7 - t_index;
sprite_data[0] = (byte)((SL_sprites_ordered[j * 4 + 1] >> t_index) & 1);
sprite_data[1] = (byte)(((SL_sprites_ordered[j * 4 + 2] >> t_index) & 1) << 1);
s_pixel = (byte)(sprite_data[0] + sprite_data[1]);
sprite_attr = (byte)SL_sprites_ordered[j * 4 + 3];
// pixel color of 0 is transparent, so if this is the case we dont have a pixel
if (s_pixel != 0)
{
have_pixel = true;
}
}
}
if (have_pixel)
{
sprite_present_list[i] = 1;
sprite_pixel_list[i] = s_pixel;
sprite_attr_list[i] = sprite_attr;
}
else
{
sprite_present_list[i] = 0;
}
}
}
public void SyncState(Serializer ser)
{
ser.Sync("LCDC", ref LCDC);
@ -1206,6 +1299,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
ser.Sync("tile_byte", ref tile_byte);
ser.Sync("sprite_fetch_cycles", ref sprite_fetch_cycles);
ser.Sync("fetch_sprite", ref fetch_sprite);
ser.Sync("fetch_sprite_01", ref fetch_sprite_01);
ser.Sync("going_to_fetch", ref going_to_fetch);
ser.Sync("sprite_fetch_counter", ref sprite_fetch_counter);
ser.Sync("sprite_attr_list", ref sprite_attr_list, false);
ser.Sync("sprite_pixel_list", ref sprite_pixel_list, false);
ser.Sync("sprite_present_list", ref sprite_present_list, false);
ser.Sync("temp_fetch", ref temp_fetch);
ser.Sync("tile_inc", ref tile_inc);
ser.Sync("pre_render", ref pre_render);
@ -1221,11 +1320,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
ser.Sync("sl_use_index", ref sl_use_index);
ser.Sync("sprite_sel", ref sprite_sel, false);
ser.Sync("no_sprites", ref no_sprites);
ser.Sync("sprite_fetch_index", ref sprite_fetch_index);
ser.Sync("evaled_sprites", ref evaled_sprites);
ser.Sync("SL_sprites_ordered", ref SL_sprites_ordered, false);
ser.Sync("index_used", ref index_used);
ser.Sync("sprite_ordered_index", ref sprite_ordered_index);
ser.Sync("bottom_index", ref bottom_index);
ser.Sync("blank_frame", ref blank_frame);
ser.Sync("window_counter", ref window_counter);