diff --git a/BizHawk.Client.Common/movie/tasproj/StateManagerDecay.cs b/BizHawk.Client.Common/movie/tasproj/StateManagerDecay.cs index 7cf5fbdccd..65156ea512 100644 --- a/BizHawk.Client.Common/movie/tasproj/StateManagerDecay.cs +++ b/BizHawk.Client.Common/movie/tasproj/StateManagerDecay.cs @@ -9,8 +9,14 @@ means that we lower the priority of a state that goes at that index. Priority changes depending on current frame and amount of states. States with biggest priority get erased first. With a 4-bit battern and no initial gap between states, total frame coverage is - about 5 times state count. Initial state gap can screw up our patterns, so do all - calculations like gap isn't there, and take it back into account afterwards. + about 5 times state count. + + Initial state gap can screw up our patterns, so do all the calculations like the gap + isn't there, and take it back into account afterwards. The algo only works with integral + greenzone, so we make it think it is integral by reducing the frame numbers. Before any + decay logic starts for each state, we check if it has a marker on it (in which case we + don't drop it) or appears inside the state gap (in which case we forcibly drop it). This + step doesn't involve numbers reduction. _zeros values are essentialy the values of rshiftby here: bitwise view frame rshiftby priority @@ -59,7 +65,7 @@ namespace BizHawk.Client.Common for (; decayStates > 0 && _tsm.StateCount > 1;) { int baseStateIndex = _tsm.GetStateIndexByFrame(Global.Emulator.Frame); - int baseStateFrame = _tsm.GetStateFrameByIndex(baseStateIndex) / _step; + int baseStateFrame = _tsm.GetStateFrameByIndex(baseStateIndex) / _step; // reduce right away int forwardPriority = -1000000; int backwardPriority = -1000000; int forwardFrame = -1; @@ -76,14 +82,18 @@ namespace BizHawk.Client.Common else if (currentFrame % _step > 0) { // ignore the pattern if the state doesn't belong already, drop it blindly and skip everything - _tsm.RemoveState(currentFrame); - decayStates--; + if (_tsm.RemoveState(currentFrame)) + { + // decrementing this if no state was removed is BAD + decayStates--; - // this is the kind of highly complex loops that might justify goto - goto next_state; + // this is the kind of highly complex loops that might justify goto + goto next_state; + } } else { + // reduce to imaginary integral greenzone for all the decay logic currentFrame /= _step; } @@ -104,7 +114,7 @@ namespace BizHawk.Client.Common for (int currentStateIndex = _tsm.StateCount - 1; currentStateIndex > baseStateIndex; currentStateIndex--) { - int currentFrame = _tsm.GetStateFrameByIndex(currentStateIndex) / _step; + int currentFrame = _tsm.GetStateFrameByIndex(currentStateIndex); if (_tsm.StateIsMarker(currentFrame)) { @@ -113,14 +123,18 @@ namespace BizHawk.Client.Common else if (currentFrame % _step > 0) { // ignore the pattern if the state doesn't belong already, drop it blindly and skip everything - _tsm.RemoveState(currentFrame); - decayStates--; + if (_tsm.RemoveState(currentFrame)) + { + // decrementing this if no state was removed is BAD + decayStates--; - // this is the kind of highly complex loops that might justify goto - goto next_state; + // this is the kind of highly complex loops that might justify goto + goto next_state; + } } else { + // reduce to imaginary integral greenzone for all the decay logic currentFrame /= _step; } @@ -143,30 +157,46 @@ namespace BizHawk.Client.Common { if (baseStateFrame - forwardFrame > backwardFrame - baseStateFrame) { - _tsm.RemoveState(forwardFrame * _step); + if (_tsm.RemoveState(forwardFrame * _step)) + { + // decrementing this if no state was removed is BAD + decayStates--; + } } else { - _tsm.RemoveState(backwardFrame * _step); + if (_tsm.RemoveState(backwardFrame * _step)) + { + // decrementing this if no state was removed is BAD + decayStates--; + } } - - decayStates--; } else if (forwardFrame > -1) { - _tsm.RemoveState(forwardFrame * _step); - decayStates--; + if (_tsm.RemoveState(forwardFrame * _step)) + { + // decrementing this if no state was removed is BAD + decayStates--; + } } else if (backwardFrame > -1) { - _tsm.RemoveState(backwardFrame * _step); - decayStates--; + if (_tsm.RemoveState(backwardFrame * _step)) + { + // decrementing this if no state was removed is BAD + decayStates--; + } } else { // we're very sorry about failing to find states to remove, but we can't go beyond capacity, so remove at least something // this shouldn't happen, but if we don't do it here, nothing good will happen either - _tsm.RemoveState(_tsm.GetStateFrameByIndex(1)); + if (_tsm.RemoveState(_tsm.GetStateFrameByIndex(1))) + { + // decrementing this if no state was removed is BAD + decayStates--; + } } // this is the kind of highly complex loops that might justify goto diff --git a/BizHawk.Client.Common/movie/tasproj/TasStateManager.cs b/BizHawk.Client.Common/movie/tasproj/TasStateManager.cs index 705325ee6c..0c02cb910b 100644 --- a/BizHawk.Client.Common/movie/tasproj/TasStateManager.cs +++ b/BizHawk.Client.Common/movie/tasproj/TasStateManager.cs @@ -262,13 +262,13 @@ namespace BizHawk.Client.Common return _movie.Markers.IsMarker(frame + 1); } - public void RemoveState(int frame) + public bool RemoveState(int frame) { int index = _states.IndexOfKey(frame); if (frame < 1 || index < 1) { - return; + return false; } StateManagerState state = _states.Values[index]; @@ -283,6 +283,8 @@ namespace BizHawk.Client.Common } _states.RemoveAt(index); + + return true; } /// diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/Execute.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/Execute.cs index f68b2689c8..c30a5675f8 100644 --- a/BizHawk.Emulation.Cores/CPUs/LR35902/Execute.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/Execute.cs @@ -4,8 +4,8 @@ namespace BizHawk.Emulation.Common.Components.LR35902 { public partial class LR35902 { - private int totalExecutedCycles; - public int TotalExecutedCycles { get { return totalExecutedCycles; } set { totalExecutedCycles = value; } } + private ulong totalExecutedCycles; + public ulong TotalExecutedCycles { get { return totalExecutedCycles; } set { totalExecutedCycles = value; } } private int EI_pending; private bool interrupts_enabled; diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs index c2f18c8f6e..208615ced4 100644 --- a/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs @@ -295,7 +295,8 @@ namespace BizHawk.Emulation.Common.Components.LR35902 // call the interrupt processor after 4 extra cycles if (!Halt_bug_3) { - INTERRUPT_GBC_NOP(); + INTERRUPT_GBC_NOP(); + //INTERRUPT_(); } else { diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs index 973b9b1fe7..a3ea968b3e 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs @@ -58,9 +58,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public override byte ReadReg(int addr) { byte ret = 0; - + //Console.WriteLine(Core.cpu.TotalExecutedCycles); switch (addr) - { + { case 0xFF40: ret = LCDC; break; // LCDC case 0xFF41: ret = STAT; break; // STAT case 0xFF42: ret = scroll_y; break; // SCY @@ -350,7 +350,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } cycle = 0; - LY_actual = LY; + LY += LY_inc; + Core.cpu.LY = LY; + // here is where LY = LYC gets cleared (but only if LY isnt 0 as that's a special case) + if (LY_inc == 1) + { + LYC_INT = false; + STAT &= 0xFB; + } no_scan = false; @@ -359,6 +366,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk LY_inc = 1; Core.in_vblank = false; + /* + VBL_INT = false; + if (STAT.Bit(3)) { HBL_INT = true; } + + STAT &= 0xFC; + */ // special note here, the y coordiate of the window is kept if the window is deactivated // meaning it will pick up where it left off if re-enabled later // so we don't reset it in the scanline loop @@ -368,8 +381,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk window_is_reset = true; } - Core.cpu.LY = LY; - // Automatically restore access to VRAM at this time (force end drawing) // Who Framed Roger Rabbit seems to run into this. VRAM_access_write = true; @@ -381,34 +392,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } } - if (cycle == 452) - { - LY += LY_inc; - - // here is where LY = LYC gets cleared (but only if LY isnt 0 as that's a special case) - if (LY_inc == 1) - { - LYC_INT = false; - STAT &= 0xFB; - } - else - { - VBL_INT = false; - if (STAT.Bit(3)) { HBL_INT = true; } - - STAT &= 0xFC; - } - - Core.cpu.LY = LY; - } - - if ((LY == 153) && (cycle == 0)) - { - LY = 0; - LY_inc = 0; - - } - // exit vblank if LCD went from off to on if (LCD_was_off) { @@ -424,25 +407,25 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // also, the LCD does not enter mode 2 on scanline 0 when first turned on no_scan = true; - cycle = 4; + cycle = 8; } // the VBL stat is continuously asserted - if ((LY_actual >= 144)) + if ((LY >= 144)) { if (STAT.Bit(4)) { - if ((cycle >= 0) && (LY_actual == 144)) + if ((cycle >= 4) && (LY == 144)) { VBL_INT = true; } - else if (LY_actual > 144) + else if (LY > 144) { VBL_INT = true; } } - if ((cycle == 0) && (LY_actual == 144)) { + if ((cycle == 4) && (LY == 144)) { HBL_INT = false; @@ -454,15 +437,16 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk Core.REG_FF0F |= 0x01; } - if ((LY_actual >= 144) && (cycle == 0)) + if ((LY >= 144) && (cycle == 4)) { // a special case of OAM mode 2 IRQ assertion, even though PPU Mode still is 1 - if (STAT.Bit(5)) { OAM_INT = true; } + //if (STAT.Bit(5)) { OAM_INT = true; } } - if ((LY_actual == 153) && (cycle == 4)) + if ((LY == 153) && (cycle == 8)) { - LY_actual = 0; + LY = 0; + LY_inc = 0; Core.cpu.LY = LY; } } @@ -475,7 +459,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // there is no mode 2 (presumably it missed the trigger) // mode 3 is very short, probably in some self test mode before turning on? - if (cycle == 4) + if (cycle == 8) { if (LY != LYC) { @@ -491,9 +475,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } } - if (cycle == 80) + if (cycle == 84) { - STAT &= 0xFC; STAT |= 0x03; OAM_INT = false; @@ -504,7 +487,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk VRAM_access_write = false; } - if (cycle == 252) + if (cycle == 256) { STAT &= 0xFC; OAM_INT = false; @@ -519,9 +502,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } else { - if (cycle < 76) + if (cycle < 80) { - if (cycle == 0) + if (cycle == 4) { // apparently, writes can make it to OAM one cycle longer then reads OAM_access_write = false; @@ -529,24 +512,22 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // here mode 2 will be set to true and interrupts fired if enabled STAT &= 0xFC; STAT |= 0x2; + if (STAT.Bit(5)) { OAM_INT = true; } HBL_INT = false; + // DMG exits VBlank into mode 0, but not GBC, so this line is needed + // (This is important for Wacky Racers and Altered Space) + VBL_INT = false; } // here OAM scanning is performed - OAM_scan(cycle + 4); + OAM_scan(cycle); } - else if (cycle >= 76 && LY_actual < 144) + else if ((cycle >= 80) && (LY < 144)) { - if (cycle == 76) - { - OAM_access_read = false; - OAM_access_write = true; - VRAM_access_read = false; - } - if (cycle == 80) + if (cycle == 84) { STAT &= 0xFC; STAT |= 0x03; @@ -556,20 +537,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } // render the screen and handle hblank - render(cycle - 76); + render(cycle - 80); } } - - if (cycle >= 452) - { - OAM_access_read = false; - OAM_scan(cycle - 452); - } } if ((LY_inc == 0)) { - if (cycle == 8) + if (cycle == 12) { LYC_INT = false; STAT &= 0xFB; @@ -583,14 +558,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } // also a special case of OAM mode 2 IRQ assertion, even though PPU Mode still is 1 - if (STAT.Bit(5)) { OAM_INT = true; } + //if (STAT.Bit(5)) { OAM_INT = true; } } - if (cycle == 88) { OAM_INT = false; } + //if (cycle == 92) { OAM_INT = false; } } // here LY=LYC will be asserted - if ((cycle == 0) && (LY_actual != 0)) + if ((cycle == 4) && (LY != 0)) { if ((LY == LYC) && !STAT.Bit(2)) { @@ -613,7 +588,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk LCD_was_off = true; LY = 0; - LY_actual = 0; Core.cpu.LY = LY; cycle = 0; @@ -647,6 +621,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // i.e. just keeping track of the lowest x-value sprite if (render_cycle == 0) { + OAM_access_read = false; + OAM_access_write = true; + VRAM_access_read = false; + // window X is latched for the scanline, mid-line changes have no effect window_x_latch = window_x; @@ -847,6 +825,17 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { read_case = 8; hbl_countdown = 5; + if (window_started) + { + hbl_countdown -= 2; + } + } + if (pixel_counter == 158 && window_started) + { + STAT &= 0xFC; + STAT |= 0x00; + + if (STAT.Bit(3)) { HBL_INT = true; } } } else if ((render_counter >= render_offset) && (pixel_counter < 0)) @@ -1411,6 +1400,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // TODO: maybe stat mode 2 flags are set at cycle 0 on visible scanlines? if (OAM_cycle == 0) { + OAM_access_read = false; + OAM_scan_index = 0; SL_sprites_index = 0; write_sprite = 0; diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IDebuggable.cs index 778b17acfc..8d5be5a227 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IDebuggable.cs @@ -71,7 +71,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public int TotalExecutedCycles { - get { return cpu.TotalExecutedCycles; } + get { return (int)cpu.TotalExecutedCycles; } } } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs index b627310025..13d5237742 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs @@ -71,7 +71,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk x_tile = (int)Math.Floor((float)(scroll_x) / 8); break; case 0xFF44: // LY - LY = LY_actual = 0; /*reset*/ + LY = 0; /*reset*/ break; case 0xFF45: // LYC LYC = value; @@ -125,7 +125,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } cycle = 0; - LY_actual = LY; + + LY += LY_inc; + Core.cpu.LY = LY; + // here is where LY = LYC gets cleared (but only if LY isnt 0 as that's a special case) + if (LY_inc == 1) + { + LYC_INT = false; + STAT &= 0xFB; + } no_scan = false; @@ -134,6 +142,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk LY_inc = 1; Core.in_vblank = false; + VBL_INT = false; + if (STAT.Bit(3)) { HBL_INT = true; } + + STAT &= 0xFC; + // special note here, the y coordiate of the window is kept if the window is deactivated // meaning it will pick up where it left off if re-enabled later // so we don't reset it in the scanline loop @@ -154,34 +167,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } } - if (cycle == 452) - { - LY += LY_inc; - - // here is where LY = LYC gets cleared (but only if LY isnt 0 as that's a special case) - if (LY_inc == 1) - { - LYC_INT = false; - STAT &= 0xFB; - } - else - { - VBL_INT = false; - if (STAT.Bit(3)) { HBL_INT = true; } - - STAT &= 0xFC; - } - - Core.cpu.LY = LY; - } - - if ((LY == 153) && (cycle == 0)) - { - LY = 0; - LY_inc = 0; - - } - // exit vblank if LCD went from off to on if (LCD_was_off) { @@ -197,25 +182,25 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // also, the LCD does not enter mode 2 on scanline 0 when first turned on no_scan = true; - cycle = 4; + cycle = 8; } // the VBL stat is continuously asserted - if ((LY_actual >= 144)) + if ((LY >= 144)) { if (STAT.Bit(4)) { - if ((cycle >= 0) && (LY_actual == 144)) + if ((cycle >= 4) && (LY == 144)) { VBL_INT = true; } - else if (LY_actual > 144) + else if (LY > 144) { VBL_INT = true; } } - if ((cycle == 0) && (LY_actual == 144)) { + if ((cycle == 4) && (LY == 144)) { HBL_INT = false; @@ -227,15 +212,16 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk Core.REG_FF0F |= 0x01; } - if ((LY_actual >= 144) && (cycle == 0)) + if ((LY >= 144) && (cycle == 4)) { // a special case of OAM mode 2 IRQ assertion, even though PPU Mode still is 1 if (STAT.Bit(5)) { OAM_INT = true; } } - if ((LY_actual == 153) && (cycle == 4)) + if ((LY == 153) && (cycle == 8)) { - LY_actual = 0; + LY = 0; + LY_inc = 0; Core.cpu.LY = LY; } } @@ -248,7 +234,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // there is no mode 2 (presumably it missed the trigger) // mode 3 is very short, probably in some self test mode before turning on? - if (cycle == 4) + if (cycle == 8) { if (LY != LYC) { @@ -264,7 +250,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } } - if (cycle == 80) + if (cycle == 84) { STAT &= 0xFC; @@ -277,7 +263,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk VRAM_access_write = false; } - if (cycle == 252) + if (cycle == 256) { STAT &= 0xFC; OAM_INT = false; @@ -292,9 +278,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } else { - if (cycle < 76) + if (cycle < 80) { - if (cycle == 0) + if (cycle == 4) { // apparently, writes can make it to OAM one cycle longer then reads OAM_access_write = false; @@ -308,18 +294,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } // here OAM scanning is performed - OAM_scan(cycle + 4); + OAM_scan(cycle); } - else if ((cycle >= 76) && (LY_actual < 144)) + else if ((cycle >= 80) && (LY < 144)) { - if (cycle == 76) - { - OAM_access_read = false; - OAM_access_write = true; - VRAM_access_read = false; - } - - if (cycle == 80) + if (cycle == 84) { STAT &= 0xFC; STAT |= 0x03; @@ -329,20 +308,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } // render the screen and handle hblank - render(cycle - 76); + render(cycle - 80); } - } - - if (cycle >= 452) - { - if (LY != 144) { OAM_access_read = false; } - OAM_scan(cycle - 452); - } + } } if ((LY_inc == 0)) { - if (cycle == 8) + if (cycle == 12) { LYC_INT = false; STAT &= 0xFB; @@ -359,11 +332,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (STAT.Bit(5)) { OAM_INT = true; } } - if (cycle == 88) { OAM_INT = false; } + if (cycle == 92) { OAM_INT = false; } } // here LY=LYC will be asserted - if ((cycle == 0) && (LY_actual != 0)) + if ((cycle == 4) && (LY != 0)) { if ((LY == LYC) && !STAT.Bit(2)) { @@ -386,7 +359,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk LCD_was_off = true; LY = 0; - LY_actual = 0; Core.cpu.LY = LY; cycle = 0; @@ -420,6 +392,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // i.e. just keeping track of the lowest x-value sprite if (render_cycle == 0) { + OAM_access_read = false; + OAM_access_write = true; + VRAM_access_read = false; + // window X is latched for the scanline, mid-line changes have no effect window_x_latch = window_x; @@ -924,14 +900,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { if (LCDC.Bit(2)) { - y = LY_actual - (SL_sprites[sl_use_index * 4] - 16); + y = LY - (SL_sprites[sl_use_index * 4] - 16); y = 15 - y; sprite_sel[0] = Core.VRAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2]; sprite_sel[1] = Core.VRAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2 + 1]; } else { - y = LY_actual - (SL_sprites[sl_use_index * 4] - 16); + y = LY - (SL_sprites[sl_use_index * 4] - 16); y = 7 - y; sprite_sel[0] = Core.VRAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2]; sprite_sel[1] = Core.VRAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2 + 1]; @@ -941,13 +917,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { if (LCDC.Bit(2)) { - y = LY_actual - (SL_sprites[sl_use_index * 4] - 16); + y = LY - (SL_sprites[sl_use_index * 4] - 16); sprite_sel[0] = Core.VRAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2]; sprite_sel[1] = Core.VRAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2 + 1]; } else { - y = LY_actual - (SL_sprites[sl_use_index * 4] - 16); + y = LY - (SL_sprites[sl_use_index * 4] - 16); sprite_sel[0] = Core.VRAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2]; sprite_sel[1] = Core.VRAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2 + 1]; } @@ -1085,6 +1061,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // TODO: maybe stat mode 2 flags are set at cycle 0 on visible scanlines? if (OAM_cycle == 0) { + OAM_access_read = false; + OAM_scan_index = 0; SL_sprites_index = 0; write_sprite = 0;