<commit_before>using System;
using System.Runtime.CompilerServices;

namespace HappyGB.Core
{
    /// <summary>
    /// Gameboy Z80 CPU.
    /// </summary>
    public partial class GBZ80
    {
        private RegisterGroup R;
        private MemoryMap M;

        private bool cpuInterruptEnable;
        private bool halted;

        private ulong localTickCount;

        public GBZ80(MemoryMap memoryMap)
        {
            R = new RegisterGroup();
            M = memoryMap;
            localTickCount = 0;
        }

        public void Reset()
        {
            R.af = 0x01B0;
            R.UpdateFlagsFromAF();
            R.bc = 0x0013;
            R.de = 0x00D8;
            R.hl = 0x014D;
            R.sp = 0xFFFE;

            if (M.BiosEnabled == false)
                R.pc  = 0x0100;
            else R.pc = 0x0000;

            cpuInterruptEnable = false;

            //Timers.
            M[0xFF05] = M[0xFF06] = M[0xFF07] = 0x00; 

            //TODO: Sound

            //Graphics.
            M[0xFF40] = 0x91; //LCDC
            M[0xFF42] = M[0xFF43] = M[0xFF45] = 0x00;  //scx scy lyc
            M[0xFF47] = 0xFC; //Palettes.
            M[0xFF48] = 0xFF;
            M[0xFF49] = 0xFF;
            M[0xFF4A] = M[0xFF4B] = 0x00; //wx wy

            M[0xFFFF] = 0x00; //IE
        }

        public bool Run(GraphicsController graphics, InterruptScheduler interrupts)
        {
            //Run until an interrupt is hit.
            //Then handle that interrupt.
            ulong startTicks;
            ulong ticksSinceLastYield = localTickCount;
            var lcdInterrupt = InterruptType.None;
            halted = false;
            while (true) {

                ///Hack: Breakpoints will be implemented actually sometime.
                //if (R.pc == 0x29A)
                //{
                //    System.Diagnostics.Debug.WriteLine("Breakpoint hit!");
                //}

                ///Hack: We don't yield until a VBlank happens. We need this to make sure the cpu
                ///doesn't lock up the main thread if interrupts get disabled.
                ///This will probably result in half-drawn frames or worse if it happens, but is 
                ///good enough for now.
                if (localTickCount - ticksSinceLastYield > (70224 * 60))
                    return false;

                //Handle interrupts.
                if (cpuInterruptEnable && ((M.IE & M.IF) != 0))
                {
                    halted = false;
                    var mf = (InterruptType)M.IF;
                    if (mf.HasFlag(InterruptType.VBlank))
                    {
                        //Handle VBlank
                        HandleInterrupt(InterruptType.VBlank);
                        //Run(graphics, interrupts);
                        return true;
                    }
                    if (mf.HasFlag(InterruptType.LCDController))
                    {
                        //Handle LCDC stat 
                        HandleInterrupt(InterruptType.LCDController);
                    }
                    if (mf.HasFlag(InterruptType.TimerOverflow))
                    {
                        //Handle timer 
                        HandleInterrupt(InterruptType.TimerOverflow);
                    }
                    if (mf.HasFlag(InterruptType.SerialComplete))
                    {
                        HandleInterrupt(InterruptType.SerialComplete);
                    }
                    if (mf.HasFlag(InterruptType.ButtonPress))
                    {
                        //Handle bpress 
                        HandleInterrupt(InterruptType.ButtonPress);
                    }
                }

                startTicks = localTickCount;
                if (halted)
                    Tick(4);
                else
                    Execute(); //Run one instruction.

                //Update the lcd controller.
                int instructionTicks = (int)(localTickCount - startTicks);
                lcdInterrupt = graphics.Update(instructionTicks);
                M.IF |= (byte)lcdInterrupt;

                //Handle interrupts in priority order.
                M.IF |= (byte)interrupts.Tick(instructionTicks);

                //TODO: Pin io.
                System.Diagnostics.Debug.WriteLine("[" + localTickCount + "] " + R.ToString());
            }
            throw new InvalidOperationException("This shouldn't have gotten here.");
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void Tick(ulong ticks)
        {
            localTickCount += ticks;
        }
<commit_msg>Fixed GBZ80 not yielding when a VBlank happens if the interrupt did not fire.<commit_after>using System;
using System.Runtime.CompilerServices;

namespace HappyGB.Core
{
    /// <summary>
    /// Gameboy Z80 CPU.
    /// </summary>
    public partial class GBZ80
    {
        private RegisterGroup R;
        private MemoryMap M;

        private bool cpuInterruptEnable;
        private bool halted;

        private ulong localTickCount;

        public GBZ80(MemoryMap memoryMap)
        {
            R = new RegisterGroup();
            M = memoryMap;
            localTickCount = 0;
        }

        public void Reset()
        {
            R.af = 0x01B0;
            R.UpdateFlagsFromAF();
            R.bc = 0x0013;
            R.de = 0x00D8;
            R.hl = 0x014D;
            R.sp = 0xFFFE;

            if (M.BiosEnabled == false)
                R.pc  = 0x0100;
            else R.pc = 0x0000;

            cpuInterruptEnable = false;

            //Timers.
            M[0xFF05] = M[0xFF06] = M[0xFF07] = 0x00; 

            //TODO: Sound

            //Graphics.
            M[0xFF40] = 0x91; //LCDC
            M[0xFF42] = M[0xFF43] = M[0xFF45] = 0x00;  //scx scy lyc
            M[0xFF47] = 0xFC; //Palettes.
            M[0xFF48] = 0xFF;
            M[0xFF49] = 0xFF;
            M[0xFF4A] = M[0xFF4B] = 0x00; //wx wy

            M[0xFFFF] = 0x00; //IE
        }

        public bool Run(GraphicsController graphics, InterruptScheduler interrupts)
        {
            //Run until an interrupt is hit.
            //Then handle that interrupt.
            ulong startTicks;
            ulong ticksSinceLastYield = localTickCount;
            var lcdInterrupt = InterruptType.None;

            halted = false; //???

            bool shouldReturn = false;
            
            while (true) {

                ///Hack: Breakpoints will be implemented actually sometime.
                //if (R.pc == 0x29A)
                //{
                //    System.Diagnostics.Debug.WriteLine("Breakpoint hit!");
                //}


                //Handle interrupts.
                if (cpuInterruptEnable && ((M.IE & M.IF) != 0))
                {
                    halted = false;
                    var mf = (InterruptType)M.IF;
                    if (mf.HasFlag(InterruptType.VBlank))
                    {
                        //Handle VBlank
                        HandleInterrupt(InterruptType.VBlank);
                    }
                    if (mf.HasFlag(InterruptType.LCDController))
                    {
                        //Handle LCDC stat 
                        HandleInterrupt(InterruptType.LCDController);
                    }
                    if (mf.HasFlag(InterruptType.TimerOverflow))
                    {
                        //Handle timer 
                        HandleInterrupt(InterruptType.TimerOverflow);
                    }
                    if (mf.HasFlag(InterruptType.SerialComplete))
                    {
                        HandleInterrupt(InterruptType.SerialComplete);
                    }
                    if (mf.HasFlag(InterruptType.ButtonPress))
                    {
                        //Handle bpress 
                        HandleInterrupt(InterruptType.ButtonPress);
                    }
                }

                startTicks = localTickCount;
                if (halted)
                    Tick(4);
                else
                    Execute(); //Run one instruction.

                //Update the lcd controller.
                int instructionTicks = (int)(localTickCount - startTicks);
                lcdInterrupt = graphics.Update(instructionTicks);
                if (lcdInterrupt.HasFlag(InterruptType.VBlank))
                {
                    shouldReturn = true;
                }
                M.IF |= (byte)lcdInterrupt;

                //Handle interrupts in priority order.
                M.IF |= (byte)interrupts.Tick(instructionTicks);

                //TODO: Pin io.
                //System.Diagnostics.Debug.WriteLine("[" + localTickCount + "] " + R.ToString());

                if (shouldReturn)
                    return true;
            }
            throw new InvalidOperationException("This shouldn't have gotten here.");
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void Tick(ulong ticks)
        {
            localTickCount += ticks;
        }

{'fraction_non_alphanumeric': 0.08931910680893192, 'fraction_numerical': 0.021449785502144977, 'mean_word_length': 1.1307710335130068, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 8, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6137881', 'n_tokens_mistral': 2586, 'n_tokens_neox': 2284, 'n_words': 523}