Ejemplo n.º 1
0
        private void RunVBlank()
        {
            if (lcdClock >= 144)
            {
                lcdClock -= 144;

                LY++; // Move to next line

                // If LY == LYC, set the flag and raise interrupt if it's enabled
                matchFlag = LY == LYC;
                if (matchFlag && (enabledInterrupts & GPUInterrupts.OnMatch) != 0)
                {
                    LCDCInterrupt?.Invoke();
                }

                if (LY == 154)
                {
                    renderTask.Wait(); // Wait for the frame to finish rendering

                    // Start rendering the next frame
                    LY = 0;
                    if (LY == LYC && (enabledInterrupts & GPUInterrupts.OnMatch) != 0)
                    {
                        LCDCInterrupt?.Invoke();
                    }

                    lcdMode = GPUMode.OAMRead;
                    if ((enabledInterrupts & GPUInterrupts.OnOAMRead) != 0)
                    {
                        LCDCInterrupt?.Invoke();
                    }
                    renderTask = Task.Run(() => LoadSpriteData());
                }
            }
        }
Ejemplo n.º 2
0
 /// <summary>
 /// LCD reading from OAM only
 /// </summary>
 private void Mode2()
 {
     if (_modeClock >= 80)
     {
         Mode = GPUMode.Mode3;
     }
 }
Ejemplo n.º 3
0
        internal void Step(uint instructionTime)
        {
            if (!LCDC.HasFlag(LCDC.LCDEnable))
            {
                return;
            }

            _modeClock += instructionTime;

            GPUMode mode = Mode;

            switch (mode)
            {
            case GPUMode.Mode0:     // HBlank
                Mode0();
                break;

            case GPUMode.Mode1:     // VBlank
                Mode1();
                break;

            case GPUMode.Mode2:     // OAM
                Mode2();
                break;

            case GPUMode.Mode3:     // LCD Transfer
                Mode3();
                break;

            default:
                break;
            }
        }
Ejemplo n.º 4
0
 /// <summary>
 /// LCD Transfer of the data from the OAM/Vram to the frame
 /// This is where the frame data is built
 /// </summary>
 private void Mode3()
 {
     if (_modeClock >= 172)
     {
         Scanline();
         Mode = GPUMode.Mode0;
     }
 }
Ejemplo n.º 5
0
        private void RunOAMRead()
        {
            if (lcdClock >= 20)
            {
                renderTask?.Wait(); // Wait for sprite load to finish
                lcdClock -= 20;

                // Pre-render the line
                lcdMode    = GPUMode.VRAMRead;
                renderTask = Task.Run(() => PreRender());
            }
        }
Ejemplo n.º 6
0
        /// <summary>
        /// VBlank
        /// Reports a new frame to display
        /// </summary>
        private void Mode1()
        {
            var line = _modeClock % 456;

            LY = (byte)(144 + line);

            if (_modeClock >= 4560)
            {
                Mode = GPUMode.Mode2;
                LY   = 0;
                VBlank?.Invoke(this, EventArgs.Empty);
            }
        }
Ejemplo n.º 7
0
        public void TestGPUMode3ToMode0Timing()
        {
            CPU cpu = new CPU(new GPU());
            MMU mmu = new MMU();
            var rom = new ROM();

            cpu.GPU.LCDC = LCDC.LCDEnable;


            decimal timeSpentMicroSec = 0;


            while (cpu.GPU.Mode != GPUMode.Mode3)
            {
                cpu.Exec(0x00);

                if (cpu.InstructionsCount > 10000)
                {
                    Assert.Fail("GPU did not changed to correct state");
                }
            }

            GPUMode gpuMode = cpu.GPU.Mode;

            while (true)
            {
                cpu.Exec(0x00);

                timeSpentMicroSec += (decimal)1.0 / cpu.ClockFrequencyMhz * cpu.LastInstructionClockTime;

                var newMode = cpu.GPU.Mode;
                if (gpuMode != newMode)
                {
                    if (newMode == GPUMode.Mode0)
                    {
                        Assert.IsTrue(timeSpentMicroSec > 41);
                        Assert.IsTrue(timeSpentMicroSec < 42M);
                        return;
                    }
                    else
                    {
                        Assert.Fail("GPU Mode should transition from Mode0 to Mode2");
                    }
                }

                if (cpu.InstructionsCount > 10000)
                {
                    Assert.Fail("GPU did not changed to correct state");
                }
            }
        }
Ejemplo n.º 8
0
        private void RunVRAMRead()
        {
            if (lcdClock >= 43)
            {
                renderTask.Wait(); // Wait for pre-rendering to finish
                lcdClock -= 43;

                // Switch to HBlank mode
                lcdMode = GPUMode.HBlank;
                if ((enabledInterrupts & GPUInterrupts.OnHBlank) != 0)
                {
                    LCDCInterrupt?.Invoke();
                }
                // No task is run during HBlank
            }
        }
Ejemplo n.º 9
0
 private void EnableLCD(bool enable)
 {
     if (lcdEnabled && !enable)
     {
         // Disable the LCD
         lcdEnabled = false;
         lcdClock   = 0;
         LY         = 0;
     }
     else if (!lcdEnabled && enable)
     {
         // Enable the LCD
         lcdEnabled = true;
         lcdMode    = GPUMode.OAMRead;
     }
 }
Ejemplo n.º 10
0
        /// <summary>
        /// HBlank
        /// </summary>
        private void Mode0()
        {
            if (_modeClock >= 204)
            {
                var y = LY++;
                if (LY > 143)
                {
                    Mode = GPUMode.Mode1;
#warning Manage VBlank interrupt ?
                }
                else
                {
                    Mode = GPUMode.Mode2;
                }
            }
        }
Ejemplo n.º 11
0
        public GPU(Clock cpuClock, GPURegisters gpuRegisters)
        {
            this.cpuClock     = cpuClock;
            this.gpuRegisters = gpuRegisters;

            clock = new Clock();

            gpuMode = GPUMode.HBlankPeriod;

            memoryData        = new byte[0x2000];
            tileSet           = new byte[0x17FF];
            tileBackgroundMap = new byte[0x7FF];
            oamData           = new byte[0xA0];
            zRamData          = new byte[0x7F];

            bmp.Save("D:\\test.bmp");
        }
Ejemplo n.º 12
0
        public void TestGPUMode0ToMode2Timing()
        {
            CPU cpu = new CPU(new GPU());
            MMU mmu = new MMU();
            var rom = new ROM();

            cpu.GPU.LCDC = LCDC.LCDEnable;
            cpu.GPU.Mode = GPUMode.Mode0;

            decimal timeSpentMicroSec = 0;

            GPUMode gpuMode = cpu.GPU.Mode;


            while (true)
            {
                cpu.Exec(0x00);

                timeSpentMicroSec += (decimal)1.0 / cpu.ClockFrequencyMhz * cpu.LastInstructionClockTime;

                var newMode = cpu.GPU.Mode;
                if (gpuMode != newMode)
                {
                    if (newMode == GPUMode.Mode2)
                    {
                        // We got the new mode, check that timings are ok
                        // Mode0 should stay up for 204 clock, meaning 48.6µsec
                        Assert.IsTrue(timeSpentMicroSec > 48.5M);
                        Assert.IsTrue(timeSpentMicroSec < 48.7M);

                        return;
                    }
                    else
                    {
                        Assert.Fail("GPU Mode should transition from Mode0 to Mode2");
                    }
                }

                if (cpu.InstructionsCount > 100000)
                {
                    Assert.Fail("GPU did not changed to correct state");
                }
            }
        }
Ejemplo n.º 13
0
        private void RunHBlank()
        {
            if (lcdClock >= 51) // Horizontal Blank lasts for ~51 m-clock ticks
            {
                lcdClock -= 51;

                LY++; // Move to next line

                // If LY == LYC, set the flag and raise interrupt if it's enabled
                matchFlag = LY == LYC;
                if (matchFlag && (enabledInterrupts & GPUInterrupts.OnMatch) != 0)
                {
                    LCDCInterrupt?.Invoke();
                }

                if (LY == 144) // Switch to vertical blank mode
                {
                    // Begin rendering the frame
                    renderTask = Task.Run(() => RenderFrame());

                    lcdMode = GPUMode.VBlank;

                    // signal vertical blank interrupt and LCD interrupt (if enabled)
                    VBlankInterrupt?.Invoke();
                    if ((enabledInterrupts & GPUInterrupts.OnVBlank) != 0)
                    {
                        LCDCInterrupt?.Invoke();
                    }
                }
                else // Start rendering the next line
                {
                    lcdMode = GPUMode.OAMRead;
                    if ((enabledInterrupts & GPUInterrupts.OnOAMRead) != 0)
                    {
                        LCDCInterrupt?.Invoke();
                    }
                    renderTask = Task.Run(() => LoadSpriteData());
                }
            }
        }
Ejemplo n.º 14
0
        public void FrameStep()
        {
            clock.IncrementCycleCount(cpuClock.LastCycleCountIncrement);

            switch (gpuMode)
            {
            case GPUMode.HBlankPeriod:
            {
                if (clock.CycleCount >= hBlankCycleDuration)
                {
                    clock.Reset();
                    gpuRegisters.CurrentScanLine++;

                    if (gpuRegisters.CurrentScanLine == lcdLinesCount)
                    {
                        gpuMode = GPUMode.VBlankPeriod;
                        //TODO draw image

                        bmp.Save("D:\\test.bmp");
                    }
                    else
                    {
                        gpuMode = GPUMode.OAMReadMode;
                    }
                }
            }
            break;

            case GPUMode.VBlankPeriod:
            {
                if (clock.CycleCount >= vBlankCycleDuration)
                {
                    clock.Reset();
                    gpuRegisters.CurrentScanLine++;

                    if (gpuRegisters.CurrentScanLine > 153)
                    {
                        gpuMode = GPUMode.OAMReadMode;
                        gpuRegisters.CurrentScanLine = 0;
                    }
                }
            }
            break;

            case GPUMode.OAMReadMode:
            {
                if (clock.CycleCount >= oamRadModeCycleDuration)
                {
                    gpuMode = GPUMode.VRAMReadMode;
                    clock.Reset();
                }
            }
            break;

            case GPUMode.VRAMReadMode:
            {
                if (clock.CycleCount >= vRadModeCycleDuration)
                {
                    gpuMode = GPUMode.HBlankPeriod;
                    clock.Reset();

                    RenderScan();
                }
            }
            break;
            }
        }
Ejemplo n.º 15
0
        public void TestGPUVBlankTiming()
        {
            CPU cpu = new CPU(new GPU());
            MMU mmu = new MMU();
            var rom = new ROM();

            cpu.GPU.LCDC = LCDC.LCDEnable;


            decimal timeSpentMicroSec = 0;

            // Wait for the first VBlank
            while (cpu.GPU.Mode != GPUMode.Mode1)
            {
                cpu.Exec(0x00);

                if (cpu.InstructionsCount > 100000)
                {
                    Assert.Fail("GPU did not changed to correct state");
                }
            }

            GPUMode gpuMode = cpu.GPU.Mode;

            var mode1ClockCount = 0;


            while (true)
            {
                cpu.Exec(0x00);

                timeSpentMicroSec += (decimal)1.0 / cpu.ClockFrequencyMhz * cpu.LastInstructionClockTime;
                mode1ClockCount   += cpu.LastInstructionClockTime;

                var newMode = cpu.GPU.Mode;
                if (gpuMode != newMode)
                {
                    if (newMode == GPUMode.Mode1)
                    {
                        Assert.AreEqual(70224, mode1ClockCount);

                        Assert.IsTrue(timeSpentMicroSec > 16700M);
                        Assert.IsTrue(timeSpentMicroSec < 16800M);

                        // VBlank frequency is around 59.73Hz
                        var vsync = 1 / (double)timeSpentMicroSec * 1000.0 * 1000.0;
                        Assert.IsTrue(vsync > 59.7);
                        Assert.IsTrue(vsync < 59.8);

                        return;
                    }
                    else if (newMode == GPUMode.Mode2 && gpuMode == GPUMode.Mode1)
                    {
                        Assert.AreEqual(4560, mode1ClockCount);
                        // Mode 1 should stay up for 1.08msec
                        Assert.IsTrue(timeSpentMicroSec > 1080M);
                        Assert.IsTrue(timeSpentMicroSec < 1090M);
                    }
                    gpuMode = newMode;
                }

                if (cpu.InstructionsCount > 1000000)
                {
                    Assert.Fail("GPU did not changed to correct state");
                }
            }
        }
Ejemplo n.º 16
0
 public GPU()
 {
     Mode = GPUMode.Mode1;
 }