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,7 +853,348 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
}
}
if (!pre_render && !fetch_sprite)
if (!fetch_sprite)
{
switch (read_case)
{
case 0: // read a background tile
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;
temp_fetch = y_tile * 32 + (x_tile + tile_inc) % 32;
tile_byte = Core.VRAM[0x1800 + (LCDC.Bit(3) ? 1 : 0) * 0x400 + temp_fetch];
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) == 0)
{
read_case_prev = 1;
y_scroll_offset = (scroll_y + LY) % 8;
if (BG_V_flip)
{
y_scroll_offset = 7 - y_scroll_offset;
}
if (LCDC.Bit(4))
{
bus_address = (VRAM_sel * 0x2000) + tile_byte * 16 + y_scroll_offset * 2;
tile_data[0] = Core.VRAM[bus_address];
}
else
{
// same as before except now tile byte represents a signed byte
if (tile_byte.Bit(7))
{
tile_byte -= 256;
}
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) == 0)
{
read_case_prev = 2;
y_scroll_offset = (scroll_y + LY) % 8;
if (BG_V_flip)
{
y_scroll_offset = 7 - y_scroll_offset;
}
if (LCDC.Bit(4))
{
// if LCDC somehow changed between the two reads, make sure we have a positive number
if (tile_byte < 0)
{
tile_byte += 256;
}
bus_address = (VRAM_sel * 0x2000) + tile_byte * 16 + y_scroll_offset * 2 + 1;
tile_data[1] = Core.VRAM[bus_address];
}
else
{
// same as before except now tile byte represents a signed byte
if (tile_byte.Bit(7) && tile_byte > 0)
{
tile_byte -= 256;
}
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
pre_render = false;
pre_render_2 = false;
// window X is latched for the scanline, mid-line changes have no effect
window_x_latch = window_x;
// x scroll is latched here, only the lower 3 bits are latched though
render_offset = scroll_offset = scroll_x % 8;
// sprite scroll offset could change depending on window usage
sprite_scroll_offset = scroll_offset;
render_counter = 0;
latch_counter = 0;
read_case = 0;
}
else
{
read_case = 3;
}
}
break;
case 3: // read from tile data
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) == 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];
bus_address = 0x3800 + (LCDC.Bit(6) ? 1 : 0) * 0x400 + temp_fetch;
tile_data[2] = Core.VRAM[bus_address];
VRAM_sel = tile_data[2].Bit(3) ? 1 : 0;
BG_V_flip = tile_data[2].Bit(6) & Core.GBC_compat;
window_tile_inc++;
}
else
{
read_case = 5;
}
window_counter++;
break;
case 5: // read from tile graphics (for the window)
if ((window_counter % 2) == 0)
{
read_case_prev = 5;
y_scroll_offset = window_y_tile_inc % 8;
if (BG_V_flip)
{
y_scroll_offset = 7 - y_scroll_offset;
}
if (LCDC.Bit(4))
{
bus_address = (VRAM_sel * 0x2000) + tile_byte * 16 + y_scroll_offset * 2;
tile_data[0] = Core.VRAM[bus_address];
}
else
{
// same as before except now tile byte represents a signed byte
if (tile_byte.Bit(7))
{
tile_byte -= 256;
}
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) == 0)
{
read_case_prev = 6;
y_scroll_offset = window_y_tile_inc % 8;
if (BG_V_flip)
{
y_scroll_offset = 7 - y_scroll_offset;
}
if (LCDC.Bit(4))
{
// if LCDC somehow changed between the two reads, make sure we have a positive number
if (tile_byte < 0)
{
tile_byte += 256;
}
bus_address = (VRAM_sel * 0x2000) + tile_byte * 16 + y_scroll_offset * 2 + 1;
tile_data[1] = Core.VRAM[bus_address];
}
else
{
// same as before except now tile byte represents a signed byte
if (tile_byte.Bit(7) && tile_byte > 0)
{
tile_byte -= 256;
}
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
// unlike for the normal background case, there is no pre-render period for the window
// so start shifting in data to the screen right away
pre_render = false;
pre_render_2 = false;
first_fetch = true;
if (window_x_latch <= 7)
{
if (scroll_offset == 0)
{
read_case = 4;
}
else
{
read_case = 8 + scroll_offset;
}
render_counter = 8 - scroll_offset;
render_offset = 7 - window_x_latch;
sprite_scroll_offset = (8 - (window_x_latch + 8 - 7) % 8) % 8;
}
else
{
render_offset = 0;
read_case = 4;
render_counter = 8;
sprite_scroll_offset = (8 - (window_x_latch - 7) % 8) % 8;
}
latch_counter = 0;
latch_new_data = true;
window_pre_render = false;
}
else
{
read_case = 7;
}
}
window_counter++;
break;
case 7: // read from tile data (window)
if ((window_counter % 2) == 0)
{
read_case_prev = 7;
// What's on the bus?
}
else
{
read_case = 4;
latch_new_data = true;
}
window_counter++;
break;
case 8: // done reading, we are now in phase 0
pre_render = true;
was_pre_render = true;
OAM_access_read = true;
OAM_access_write = true;
VRAM_access_read = true;
VRAM_access_write = true;
read_case = 18;
break;
case 9:
// this is a degenerate case for starting the window at 0
// kevtris' timing doc indicates an additional normal BG access
// but this information is thrown away, so it's faster to do this then constantly check
// for it in read case 0
read_case = 4;
break;
case 10:
case 11:
case 12:
case 13:
case 14:
case 15:
case 16:
case 17:
read_case--;
break;
case 18:
case 19:
case 20:
read_case++;
break;
case 21:
// hardware tests indicate that HDMA starts at this point, not immediately after mode 3 ends
rendering_complete = true;
HDMA_can_start = true;
break;
}
if (!was_pre_render)
{
// start shifting data into the LCD
if (render_counter >= (render_offset + 8))
@ -976,297 +1318,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
if (pixel_counter == 160)
{
read_case = 8;
hbl_countdown = 2;
}
}
else if (pixel_counter < 0)
{
pixel_counter++;
}
render_counter++;
}
// hbl_countdown = 1;
if (!fetch_sprite)
{
switch (read_case)
{
case 0: // read a background tile
if ((internal_cycle % 2) == 1)
{
// calculate the row number of the tiles to be fetched
y_tile = (((int)scroll_y + LY) >> 3) % 32;
x_tile = scroll_x >> 3;
temp_fetch = y_tile * 32 + (x_tile + tile_inc) % 32;
tile_byte = Core.VRAM[0x1800 + (LCDC.Bit(3) ? 1 : 0) * 0x400 + temp_fetch];
bus_address = 0x3800 + (LCDC.Bit(3) ? 1 : 0) * 0x400 + temp_fetch;
tile_data[2] = Core.VRAM[bus_address];
VRAM_sel = tile_data[2].Bit(3) ? 1 : 0;
BG_V_flip = tile_data[2].Bit(6) & Core.GBC_compat;
read_case = 1;
if (!pre_render)
{
tile_inc++;
}
}
break;
case 1: // read from tile graphics (0)
if ((internal_cycle % 2) == 1)
{
y_scroll_offset = (scroll_y + LY) % 8;
if (BG_V_flip)
{
y_scroll_offset = 7 - y_scroll_offset;
}
if (LCDC.Bit(4))
{
bus_address = (VRAM_sel * 0x2000) + tile_byte * 16 + y_scroll_offset * 2;
tile_data[0] = Core.VRAM[bus_address];
}
else
{
// same as before except now tile byte represents a signed byte
if (tile_byte.Bit(7))
{
tile_byte -= 256;
}
bus_address = (VRAM_sel * 0x2000) + 0x1000 + tile_byte * 16 + y_scroll_offset * 2;
tile_data[0] = Core.VRAM[bus_address];
}
read_case = 2;
}
break;
case 2: // read from tile graphics (1)
if ((internal_cycle % 2) == 1)
{
y_scroll_offset = (scroll_y + LY) % 8;
if (BG_V_flip)
{
y_scroll_offset = 7 - y_scroll_offset;
}
if (LCDC.Bit(4))
{
// if LCDC somehow changed between the two reads, make sure we have a positive number
if (tile_byte < 0)
{
tile_byte += 256;
}
bus_address = (VRAM_sel * 0x2000) + tile_byte * 16 + y_scroll_offset * 2 + 1;
tile_data[1] = Core.VRAM[bus_address];
}
else
{
// same as before except now tile byte represents a signed byte
if (tile_byte.Bit(7) && tile_byte > 0)
{
tile_byte -= 256;
}
bus_address = (VRAM_sel * 0x2000) + 0x1000 + tile_byte * 16 + y_scroll_offset * 2 + 1;
tile_data[1] = Core.VRAM[bus_address];
}
if (pre_render)
{
// here we set up rendering
pre_render = false;
pre_render_2 = false;
// window X is latched for the scanline, mid-line changes have no effect
window_x_latch = window_x;
// x scroll is latched here, only the lower 3 bits are latched though
render_offset = scroll_offset = scroll_x % 8;
// sprite scroll offset could change depending on window usage
sprite_scroll_offset = scroll_offset;
render_counter = 0;
latch_counter = 0;
read_case = 0;
}
else
{
read_case = 3;
}
}
break;
case 3: // read from tile data
if ((internal_cycle % 2) == 1)
{
read_case = 0;
latch_new_data = true;
}
break;
case 4: // read from window data
if ((window_counter % 2) == 1)
{
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];
bus_address = 0x3800 + (LCDC.Bit(6) ? 1 : 0) * 0x400 + temp_fetch;
tile_data[2] = Core.VRAM[bus_address];
VRAM_sel = tile_data[2].Bit(3) ? 1 : 0;
BG_V_flip = tile_data[2].Bit(6) & Core.GBC_compat;
window_tile_inc++;
read_case = 5;
}
window_counter++;
break;
case 5: // read from tile graphics (for the window)
if ((window_counter % 2) == 1)
{
y_scroll_offset = window_y_tile_inc % 8;
if (BG_V_flip)
{
y_scroll_offset = 7 - y_scroll_offset;
}
if (LCDC.Bit(4))
{
bus_address = (VRAM_sel * 0x2000) + tile_byte * 16 + y_scroll_offset * 2;
tile_data[0] = Core.VRAM[bus_address];
}
else
{
// same as before except now tile byte represents a signed byte
if (tile_byte.Bit(7))
{
tile_byte -= 256;
}
bus_address = (VRAM_sel * 0x2000) + 0x1000 + tile_byte * 16 + y_scroll_offset * 2;
tile_data[0] = Core.VRAM[bus_address];
}
read_case = 6;
}
window_counter++;
break;
case 6: // read from tile graphics (for the window)
if ((window_counter % 2) == 1)
{
y_scroll_offset = window_y_tile_inc % 8;
if (BG_V_flip)
{
y_scroll_offset = 7 - y_scroll_offset;
}
if (LCDC.Bit(4))
{
// if LCDC somehow changed between the two reads, make sure we have a positive number
if (tile_byte < 0)
{
tile_byte += 256;
}
bus_address = (VRAM_sel * 0x2000) + tile_byte * 16 + y_scroll_offset * 2 + 1;
tile_data[1] = Core.VRAM[bus_address];
}
else
{
// same as before except now tile byte represents a signed byte
if (tile_byte.Bit(7) && tile_byte > 0)
{
tile_byte -= 256;
}
bus_address = (VRAM_sel * 0x2000) + 0x1000 + tile_byte * 16 + y_scroll_offset * 2 + 1;
tile_data[1] = Core.VRAM[bus_address];
}
if (window_pre_render)
{
// here we set up rendering
// unlike for the normal background case, there is no pre-render period for the window
// so start shifting in data to the screen right away
pre_render = false;
pre_render_2 = false;
first_fetch = true;
if (window_x_latch <= 7)
{
if (scroll_offset == 0)
{
read_case = 4;
}
else
{
read_case = 8 + scroll_offset;
}
render_counter = 8 - scroll_offset;
render_offset = 7 - window_x_latch;
sprite_scroll_offset = (8 - (window_x_latch + 8 - 7) % 8) % 8;
}
else
{
render_offset = 0;
read_case = 4;
render_counter = 8;
sprite_scroll_offset = (8 - (window_x_latch - 7) % 8) % 8;
}
latch_counter = 0;
latch_new_data = true;
window_pre_render = false;
}
else
{
read_case = 7;
}
}
window_counter++;
break;
case 7: // read from tile data (window)
if ((window_counter % 2) == 1)
{
read_case = 4;
latch_new_data = true;
}
window_counter++;
break;
case 8: // done reading, we are now in phase 0
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;
read_case = 18;
}
else
{
STAT &= 0xFC;
STAT |= 0x00;
@ -1285,36 +1338,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
}
}
}
break;
case 9:
// this is a degenerate case for starting the window at 0
// kevtris' timing doc indicates an additional normal BG access
// but this information is thrown away, so it's faster to do this then constantly check
// for it in read case 0
read_case = 4;
break;
case 10:
case 11:
case 12:
case 13:
case 14:
case 15:
case 16:
case 17:
read_case--;
break;
case 18:
case 19:
case 20:
read_case++;
break;
case 21:
// hardware tests indicate that HDMA starts at this point, not immediately after mode 3 ends
rendering_complete = true;
HDMA_can_start = true;
break;
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,7 +850,355 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
}
}
if (!pre_render && !fetch_sprite)
if (!fetch_sprite)
{
switch (read_case)
{
case 0: // read a background tile
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;
temp_fetch = y_tile * 32 + (x_tile + tile_inc) % 32;
tile_byte = Core.VRAM[0x1800 + (LCDC.Bit(3) ? 1 : 0) * 0x400 + temp_fetch];
bus_address = 0x3800 + (LCDC.Bit(3) ? 1 : 0) * 0x400 + temp_fetch;
tile_data[2] = Core.VRAM[bus_address];
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) == 0)
{
read_case_prev = 1;
y_scroll_offset = (scroll_y + LY) % 8;
if (BG_V_flip)
{
y_scroll_offset = 7 - y_scroll_offset;
}
if (LCDC.Bit(4))
{
bus_address = (VRAM_sel * 0x2000) + tile_byte * 16 + y_scroll_offset * 2;
tile_data[0] = Core.VRAM[bus_address];
}
else
{
// same as before except now tile byte represents a signed byte
if (tile_byte.Bit(7))
{
tile_byte -= 256;
}
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) == 0)
{
read_case_prev = 2;
y_scroll_offset = (scroll_y + LY) % 8;
if (BG_V_flip)
{
y_scroll_offset = 7 - y_scroll_offset;
}
if (LCDC.Bit(4))
{
// if LCDC somehow changed between the two reads, make sure we have a positive number
if (tile_byte < 0)
{
tile_byte += 256;
}
bus_address = (VRAM_sel * 0x2000) + tile_byte * 16 + y_scroll_offset * 2 + 1;
tile_data[1] = Core.VRAM[bus_address];
}
else
{
// same as before except now tile byte represents a signed byte
if (tile_byte.Bit(7) && tile_byte > 0)
{
tile_byte -= 256;
}
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
pre_render = false;
pre_render_2 = false;
// window X is latched for the scanline, mid-line changes have no effect
window_x_latch = window_x;
// x scroll is latched here, only the lower 3 bits are latched though
render_offset = scroll_offset = scroll_x % 8;
// sprite scroll offset could change depending on window usage
sprite_scroll_offset = scroll_offset;
render_counter = 0;
latch_counter = 0;
read_case = 0;
}
else
{
read_case = 3;
}
}
break;
case 3: // read from tile data
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) == 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];
bus_address = 0x3800 + (LCDC.Bit(6) ? 1 : 0) * 0x400 + temp_fetch;
tile_data[2] = Core.VRAM[bus_address];
VRAM_sel = tile_data[2].Bit(3) ? 1 : 0;
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) == 0)
{
read_case_prev = 5;
y_scroll_offset = window_y_tile_inc % 8;
if (BG_V_flip)
{
y_scroll_offset = 7 - y_scroll_offset;
}
if (LCDC.Bit(4))
{
bus_address = (VRAM_sel * 0x2000) + tile_byte * 16 + y_scroll_offset * 2;
tile_data[0] = Core.VRAM[bus_address];
}
else
{
// same as before except now tile byte represents a signed byte
if (tile_byte.Bit(7))
{
tile_byte -= 256;
}
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) == 0)
{
read_case_prev = 6;
y_scroll_offset = window_y_tile_inc % 8;
if (BG_V_flip)
{
y_scroll_offset = 7 - y_scroll_offset;
}
if (LCDC.Bit(4))
{
// if LCDC somehow changed between the two reads, make sure we have a positive number
if (tile_byte < 0)
{
tile_byte += 256;
}
bus_address = (VRAM_sel * 0x2000) + tile_byte * 16 + y_scroll_offset * 2 + 1;
tile_data[1] = Core.VRAM[bus_address];
}
else
{
// same as before except now tile byte represents a signed byte
if (tile_byte.Bit(7) && tile_byte > 0)
{
tile_byte -= 256;
}
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
// unlike for the normal background case, there is no pre-render period for the window
// so start shifting in data to the screen right away
pre_render = false;
pre_render_2 = false;
first_fetch = true;
if (window_x_latch <= 7)
{
if (scroll_offset == 0)
{
read_case = 4;
}
else
{
read_case = 8 + scroll_offset;
}
render_counter = 8 - scroll_offset;
render_offset = 7 - window_x_latch;
sprite_scroll_offset = (8 - (window_x_latch + 8 - 7) % 8) % 8;
}
else
{
render_offset = 0;
read_case = 4;
render_counter = 8;
sprite_scroll_offset = (8 - (window_x_latch - 7) % 8) % 8;
}
latch_counter = 0;
latch_new_data = true;
window_pre_render = false;
}
else
{
read_case = 7;
}
}
window_counter++;
break;
case 7: // read from tile data (window)
if ((window_counter % 2) == 0)
{
read_case_prev = 7;
// What's on the bus?
}
else
{
read_case = 4;
latch_new_data = true;
}
window_counter++;
break;
case 8: // done reading, we are now in phase 0
pre_render = true;
was_pre_render = true;
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
}
break;
case 9:
// this is a degenerate case for starting the window at 0
// kevtris' timing doc indicates an additional normal BG access
// but this information is thrown away, so it's faster to do this then constantly check
// for it in read case 0
read_case = 4;
break;
case 10:
case 11:
case 12:
case 13:
case 14:
case 15:
case 16:
case 17:
read_case--;
break;
case 18:
case 19:
case 20:
read_case++;
break;
case 21:
// hardware tests indicate that HDMA starts at this point, not immediately after mode 3 ends
rendering_complete = true;
HDMA_can_start = true;
break;
}
if (!was_pre_render)
{
// start shifting data into the LCD
if (render_counter >= (render_offset + 8))
@ -932,306 +1282,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
if (pixel_counter == 160)
{
read_case = 8;
hbl_countdown = 2;
}
}
else if (pixel_counter < 0)
{
pixel_counter++;
}
render_counter++;
}
// hbl_countdown = 1;
if (!fetch_sprite)
{
switch (read_case)
{
case 0: // read a background tile
if ((internal_cycle % 2) == 1)
{
// calculate the row number of the tiles to be fetched
y_tile = (((int)scroll_y + LY) >> 3) % 32;
x_tile = scroll_x >> 3;
temp_fetch = y_tile * 32 + (x_tile + tile_inc) % 32;
tile_byte = Core.VRAM[0x1800 + (LCDC.Bit(3) ? 1 : 0) * 0x400 + temp_fetch];
bus_address = 0x3800 + (LCDC.Bit(3) ? 1 : 0) * 0x400 + temp_fetch;
tile_data[2] = Core.VRAM[bus_address];
VRAM_sel = tile_data[2].Bit(3) ? 1 : 0;
BG_V_flip = tile_data[2].Bit(6);
read_case = 1;
if (!pre_render)
{
tile_inc++;
}
}
break;
case 1: // read from tile graphics (0)
if ((internal_cycle % 2) == 1)
{
y_scroll_offset = (scroll_y + LY) % 8;
if (BG_V_flip)
{
y_scroll_offset = 7 - y_scroll_offset;
}
if (LCDC.Bit(4))
{
bus_address = (VRAM_sel * 0x2000) + tile_byte * 16 + y_scroll_offset * 2;
tile_data[0] = Core.VRAM[bus_address];
}
else
{
// same as before except now tile byte represents a signed byte
if (tile_byte.Bit(7))
{
tile_byte -= 256;
}
bus_address = (VRAM_sel * 0x2000) + 0x1000 + tile_byte * 16 + y_scroll_offset * 2;
tile_data[0] = Core.VRAM[bus_address];
}
read_case = 2;
}
break;
case 2: // read from tile graphics (1)
if ((internal_cycle % 2) == 1)
{
y_scroll_offset = (scroll_y + LY) % 8;
if (BG_V_flip)
{
y_scroll_offset = 7 - y_scroll_offset;
}
if (LCDC.Bit(4))
{
// if LCDC somehow changed between the two reads, make sure we have a positive number
if (tile_byte < 0)
{
tile_byte += 256;
}
bus_address = (VRAM_sel * 0x2000) + tile_byte * 16 + y_scroll_offset * 2 + 1;
tile_data[1] = Core.VRAM[bus_address];
}
else
{
// same as before except now tile byte represents a signed byte
if (tile_byte.Bit(7) && tile_byte > 0)
{
tile_byte -= 256;
}
bus_address = (VRAM_sel * 0x2000) + 0x1000 + tile_byte * 16 + y_scroll_offset * 2 + 1;
tile_data[1] = Core.VRAM[bus_address];
}
if (pre_render)
{
// here we set up rendering
pre_render = false;
pre_render_2 = false;
// window X is latched for the scanline, mid-line changes have no effect
window_x_latch = window_x;
// x scroll is latched here, only the lower 3 bits are latched though
render_offset = scroll_offset = scroll_x % 8;
// sprite scroll offset could change depending on window usage
sprite_scroll_offset = scroll_offset;
render_counter = 0;
latch_counter = 0;
read_case = 0;
}
else
{
read_case = 3;
}
}
break;
case 3: // read from tile data
if ((internal_cycle % 2) == 1)
{
read_case = 0;
latch_new_data = true;
}
break;
case 4: // read from window data
if ((window_counter % 2) == 1)
{
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];
bus_address = 0x3800 + (LCDC.Bit(6) ? 1 : 0) * 0x400 + temp_fetch;
tile_data[2] = Core.VRAM[bus_address];
VRAM_sel = tile_data[2].Bit(3) ? 1 : 0;
BG_V_flip = tile_data[2].Bit(6);
window_tile_inc++;
read_case = 5;
}
window_counter++;
break;
case 5: // read from tile graphics (for the window)
if ((window_counter % 2) == 1)
{
y_scroll_offset = window_y_tile_inc % 8;
if (BG_V_flip)
{
y_scroll_offset = 7 - y_scroll_offset;
}
if (LCDC.Bit(4))
{
bus_address = (VRAM_sel * 0x2000) + tile_byte * 16 + y_scroll_offset * 2;
tile_data[0] = Core.VRAM[bus_address];
}
else
{
// same as before except now tile byte represents a signed byte
if (tile_byte.Bit(7))
{
tile_byte -= 256;
}
bus_address = (VRAM_sel * 0x2000) + 0x1000 + tile_byte * 16 + y_scroll_offset * 2;
tile_data[0] = Core.VRAM[bus_address];
}
read_case = 6;
}
window_counter++;
break;
case 6: // read from tile graphics (for the window)
if ((window_counter % 2) == 1)
{
y_scroll_offset = window_y_tile_inc % 8;
if (BG_V_flip)
{
y_scroll_offset = 7 - y_scroll_offset;
}
if (LCDC.Bit(4))
{
// if LCDC somehow changed between the two reads, make sure we have a positive number
if (tile_byte < 0)
{
tile_byte += 256;
}
bus_address = (VRAM_sel * 0x2000) + tile_byte * 16 + y_scroll_offset * 2 + 1;
tile_data[1] = Core.VRAM[bus_address];
}
else
{
// same as before except now tile byte represents a signed byte
if (tile_byte.Bit(7) && tile_byte > 0)
{
tile_byte -= 256;
}
bus_address = (VRAM_sel * 0x2000) + 0x1000 + tile_byte * 16 + y_scroll_offset * 2 + 1;
tile_data[1] = Core.VRAM[bus_address];
}
if (window_pre_render)
{
// here we set up rendering
// unlike for the normal background case, there is no pre-render period for the window
// so start shifting in data to the screen right away
pre_render = false;
pre_render_2 = false;
first_fetch = true;
if (window_x_latch <= 7)
{
if (scroll_offset == 0)
{
read_case = 4;
}
else
{
read_case = 8 + scroll_offset;
}
render_counter = 8 - scroll_offset;
render_offset = 7 - window_x_latch;
sprite_scroll_offset = (8 - (window_x_latch + 8 - 7) % 8) % 8;
}
else
{
render_offset = 0;
read_case = 4;
render_counter = 8;
sprite_scroll_offset = (8 - (window_x_latch - 7) % 8) % 8;
}
latch_counter = 0;
latch_new_data = true;
window_pre_render = false;
}
else
{
read_case = 7;
}
}
window_counter++;
break;
case 7: // read from tile data (window)
if ((window_counter % 2) == 1)
{
read_case = 4;
latch_new_data = true;
}
window_counter++;
break;
case 8: // done reading, we are now in phase 0
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;
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;
@ -1253,36 +1305,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
}
}
}
break;
case 9:
// this is a degenerate case for starting the window at 0
// kevtris' timing doc indicates an additional normal BG access
// but this information is thrown away, so it's faster to do this then constantly check
// for it in read case 0
read_case = 4;
break;
case 10:
case 11:
case 12:
case 13:
case 14:
case 15:
case 16:
case 17:
read_case--;
break;
case 18:
case 19:
case 20:
read_case++;
break;
case 21:
// hardware tests indicate that HDMA starts at this point, not immediately after mode 3 ends
rendering_complete = true;
HDMA_can_start = true;
break;
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,7 +538,312 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
}
}
if (!pre_render && !fetch_sprite)
if (!fetch_sprite)
{
switch (read_case)
{
case 0: // read a background tile
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;
temp_fetch = y_tile * 32 + (x_tile + tile_inc) % 32;
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) == 0)
{
read_case_prev = 1;
y_scroll_offset = (scroll_y + LY) % 8;
if (LCDC.Bit(4))
{
bus_address = tile_byte * 16 + y_scroll_offset * 2;
tile_data[0] = Core.VRAM[bus_address];
}
else
{
// same as before except now tile byte represents a signed byte
if (tile_byte.Bit(7))
{
tile_byte -= 256;
}
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) == 0)
{
read_case_prev = 2;
y_scroll_offset = (scroll_y + LY) % 8;
if (LCDC.Bit(4))
{
// if LCDC somehow changed between the two reads, make sure we have a positive number
if (tile_byte < 0)
{
tile_byte += 256;
}
bus_address = tile_byte * 16 + y_scroll_offset * 2 + 1;
tile_data[1] = Core.VRAM[bus_address];
}
else
{
// same as before except now tile byte represents a signed byte
if (tile_byte.Bit(7) && tile_byte > 0)
{
tile_byte -= 256;
}
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
pre_render = false;
pre_render_2 = false;
// window X is latched for the scanline, mid-line changes have no effect
window_x_latch = window_x;
// x scroll is latched here
render_offset = scroll_offset = scroll_x % 8;
// sprite scroll offset could change depending on window usage
sprite_scroll_offset = scroll_offset;
render_counter = 0;
latch_counter = 0;
read_case = 0;
}
else
{
read_case = 3;
}
}
break;
case 3: // read from tile data
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) == 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) == 0)
{
read_case_prev = 5;
y_scroll_offset = (window_y_tile_inc) % 8;
if (LCDC.Bit(4))
{
bus_address = tile_byte * 16 + y_scroll_offset * 2;
tile_data[0] = Core.VRAM[bus_address];
}
else
{
// same as before except now tile byte represents a signed byte
if (tile_byte.Bit(7))
{
tile_byte -= 256;
}
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) == 0)
{
read_case_prev = 6;
y_scroll_offset = (window_y_tile_inc) % 8;
if (LCDC.Bit(4))
{
// if LCDC somehow changed between the two reads, make sure we have a positive number
if (tile_byte < 0)
{
tile_byte += 256;
}
bus_address = tile_byte * 16 + y_scroll_offset * 2 + 1;
tile_data[1] = Core.VRAM[bus_address];
}
else
{
// same as before except now tile byte represents a signed byte
if (tile_byte.Bit(7) && tile_byte > 0)
{
tile_byte -= 256;
}
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
// unlike for the normal background case, there is no pre-render period for the window
// so start shifting in data to the screen right away
pre_render = false;
pre_render_2 = false;
first_fetch = true;
if (window_x_latch <= 7)
{
if (scroll_offset == 0)
{
read_case = 4;
}
else
{
read_case = 8 + scroll_offset;
}
render_counter = 8 - scroll_offset;
render_offset = 7 - window_x_latch;
sprite_scroll_offset = (8 - (window_x_latch + 8 - 7) % 8) % 8;
}
else
{
render_offset = 0;
read_case = 4;
render_counter = 8;
sprite_scroll_offset = (8 - (window_x_latch - 7) % 8) % 8;
}
latch_counter = 0;
latch_new_data = true;
window_pre_render = false;
}
else
{
read_case = 7;
}
}
window_counter++;
break;
case 7: // read from tile data (window)
if ((window_counter % 2) == 0)
{
read_case_prev = 7;
// What's on the bus?
}
else
{
read_case = 4;
latch_new_data = true;
}
window_counter++;
break;
case 8: // done reading, we are now in phase 0
pre_render = true;
was_pre_render = true;
VRAM_access_read = true;
VRAM_access_write = true;
OAM_access_read = true;
OAM_access_write = true;
read_case = 18;
break;
case 9:
// this is a degenerate case for starting the window at 0
// kevtris' timing doc indicates an additional normal BG access
// but this information is thrown away, so it's faster to do this then constantly check
// for it in read case 0
read_case = 4;
break;
case 10:
case 11:
case 12:
case 13:
case 14:
case 15:
case 16:
case 17:
read_case--;
break;
case 18:
rendering_complete = true;
break;
}
if (!was_pre_render)
{
// start shifting data into the LCD
if (render_counter >= (render_offset + 8))
@ -608,270 +915,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
if (pixel_counter == 160)
{
hbl_countdown = 2;
read_case = 8;
}
}
else if (pixel_counter < 0)
{
pixel_counter++;
}
render_counter++;
}
// hbl_countdown = 1;
if (!fetch_sprite)
{
switch (read_case)
{
case 0: // read a background tile
if ((internal_cycle % 2) == 1)
{
// calculate the row number of the tiles to be fetched
y_tile = (((int)scroll_y + LY) >> 3) % 32;
x_tile = scroll_x >> 3;
temp_fetch = y_tile * 32 + (x_tile + tile_inc) % 32;
bus_address = 0x1800 + (LCDC.Bit(3) ? 1 : 0) * 0x400 + temp_fetch;
tile_byte = Core.VRAM[bus_address];
read_case = 1;
if (!pre_render)
{
tile_inc++;
}
}
break;
case 1: // read from tile graphics (0)
if ((internal_cycle % 2) == 1)
{
y_scroll_offset = (scroll_y + LY) % 8;
if (LCDC.Bit(4))
{
bus_address = tile_byte * 16 + y_scroll_offset * 2;
tile_data[0] = Core.VRAM[bus_address];
}
else
{
// same as before except now tile byte represents a signed byte
if (tile_byte.Bit(7))
{
tile_byte -= 256;
}
bus_address = 0x1000 + tile_byte * 16 + y_scroll_offset * 2;
tile_data[0] = Core.VRAM[bus_address];
}
read_case = 2;
}
break;
case 2: // read from tile graphics (1)
if ((internal_cycle % 2) == 1)
{
y_scroll_offset = (scroll_y + LY) % 8;
if (LCDC.Bit(4))
{
// if LCDC somehow changed between the two reads, make sure we have a positive number
if (tile_byte < 0)
{
tile_byte += 256;
}
bus_address = tile_byte * 16 + y_scroll_offset * 2 + 1;
tile_data[1] = Core.VRAM[bus_address];
}
else
{
// same as before except now tile byte represents a signed byte
if (tile_byte.Bit(7) && tile_byte > 0)
{
tile_byte -= 256;
}
bus_address = 0x1000 + tile_byte * 16 + y_scroll_offset * 2 + 1;
tile_data[1] = Core.VRAM[bus_address];
}
if (pre_render)
{
// here we set up rendering
pre_render = false;
pre_render_2 = false;
// window X is latched for the scanline, mid-line changes have no effect
window_x_latch = window_x;
// x scroll is latched here
render_offset = scroll_offset = scroll_x % 8;
// sprite scroll offset could change depending on window usage
sprite_scroll_offset = scroll_offset;
render_counter = 0;
latch_counter = 0;
read_case = 0;
}
else
{
read_case = 3;
}
}
break;
case 3: // read from tile data
if ((internal_cycle % 2) == 1)
{
read_case = 0;
latch_new_data = true;
}
break;
case 4: // read from window data
if ((window_counter % 2) == 1)
{
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++;
read_case = 5;
}
window_counter++;
break;
case 5: // read from tile graphics (for the window)
if ((window_counter % 2) == 1)
{
y_scroll_offset = (window_y_tile_inc) % 8;
if (LCDC.Bit(4))
{
bus_address = tile_byte * 16 + y_scroll_offset * 2;
tile_data[0] = Core.VRAM[bus_address];
}
else
{
// same as before except now tile byte represents a signed byte
if (tile_byte.Bit(7))
{
tile_byte -= 256;
}
bus_address = 0x1000 + tile_byte * 16 + y_scroll_offset * 2;
tile_data[0] = Core.VRAM[bus_address];
}
read_case = 6;
}
window_counter++;
break;
case 6: // read from tile graphics (for the window)
if ((window_counter % 2) == 1)
{
y_scroll_offset = (window_y_tile_inc) % 8;
if (LCDC.Bit(4))
{
// if LCDC somehow changed between the two reads, make sure we have a positive number
if (tile_byte < 0)
{
tile_byte += 256;
}
bus_address = tile_byte * 16 + y_scroll_offset * 2 + 1;
tile_data[1] = Core.VRAM[bus_address];
}
else
{
// same as before except now tile byte represents a signed byte
if (tile_byte.Bit(7) && tile_byte > 0)
{
tile_byte -= 256;
}
bus_address = 0x1000 + tile_byte * 16 + y_scroll_offset * 2 + 1;
tile_data[1] = Core.VRAM[bus_address];
}
if (window_pre_render)
{
// here we set up rendering
// unlike for the normal background case, there is no pre-render period for the window
// so start shifting in data to the screen right away
pre_render = false;
pre_render_2 = false;
first_fetch = true;
if (window_x_latch <= 7)
{
if (scroll_offset == 0)
{
read_case = 4;
}
else
{
read_case = 8 + scroll_offset;
}
render_counter = 8 - scroll_offset;
render_offset = 7 - window_x_latch;
sprite_scroll_offset = (8 - (window_x_latch + 8 - 7) % 8) % 8;
}
else
{
render_offset = 0;
read_case = 4;
render_counter = 8;
sprite_scroll_offset = (8 - (window_x_latch - 7) % 8) % 8;
}
latch_counter = 0;
latch_new_data = true;
window_pre_render = false;
}
else
{
read_case = 7;
}
}
window_counter++;
break;
case 7: // read from tile data (window)
if ((window_counter % 2) == 1)
{
read_case = 4;
latch_new_data = true;
}
window_counter++;
break;
case 8: // done reading, we are now in phase 0
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;
read_case = 18;
}
else
{
STAT &= 0xFC;
STAT |= 0x00;
@ -890,29 +936,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
}
}
}
break;
case 9:
// this is a degenerate case for starting the window at 0
// kevtris' timing doc indicates an additional normal BG access
// but this information is thrown away, so it's faster to do this then constantly check
// for it in read case 0
read_case = 4;
break;
case 10:
case 11:
case 12:
case 13:
case 14:
case 15:
case 16:
case 17:
read_case--;
break;
case 18:
rendering_complete = true;
break;
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);
}
}
}