Ejemplo n.º 1
0
        private void ChangeMode(LcdMode newMode)
        {
            _currentMode = newMode;
            _counter     = 0;

            if (newMode == LcdMode.OamSearch)
            {
                if (_stat.OamSearchInterruptEnabled.Value)
                {
                    _lcdStatusInterrupt.Trigger();
                }
            }
            else if (newMode == LcdMode.DataTransfer)
            {
                // Do nothing.
            }
            else if (newMode == LcdMode.HorizontalBlank)
            {
                RenderScanline(_ly.Value);

                if (_stat.HBlankInterruptEnabled.Value)
                {
                    _lcdStatusInterrupt.Trigger();
                }
            }
            else if (newMode == LcdMode.VerticalBlank)
            {
                //Console.WriteLine($"Vblank after {_globalCounter - _lastVblank} cycles.");
                _lastVblank = _globalCounter;


                OnCompletedFrame();

                _vblankInterrupt.Trigger();

                // TODO does vblank really have two interrupts?
                if (_stat.VBlankInterruptEnabled.Value)
                {
                    _vblankInterrupt.Trigger();
                }
            }
        }
Ejemplo n.º 2
0
 void SetStatLcdMode(LcdMode mode)
 {
     stat = (byte)((stat & 0xFC) | (byte)mode);
 }
Ejemplo n.º 3
0
 void SetLcdMode(LcdMode mode)
 {
     this.lcdMode = mode;
     SetStatLcdMode(mode);
 }
Ejemplo n.º 4
0
        public override void Update(uint cycleCount)
        {
            if (_oamDma.Status != OamDmaStatus.Inactive)
            {
                UpdateOamDma(cycleCount);
            }

            // only run lcd controller update if display is enabled
            if (!_displayEnabled)
            {
                return;
            }

            _frameClock += cycleCount;

            // LCD runs for 70224 cpu cycles a frame, 456 cycles a scanline and 154 total scanlines
            // 10 of the scanlines are VBlank and the rest are the active portion of the cycle
            // mode 2, 3 and 0 occur in he first 65664 cycles of a frame (70224 - 4560)
            if (_frameClock < 65664)
            {
                // Mode 2 - OAM read, 77-83 cycles of frame
                if ((_frameClock % 456) < 80)
                {
                    if (_lcdMode != LcdMode.OamRead)
                    {
                        // We've just entered OAM read so we're at the beginning of the line,
                        // increment the current scanline unless we're coming from VBlank, then
                        // don't inrecement because we've just set it to 0
                        if (_lcdMode != LcdMode.VBlank)
                        {
                            _currentScanline++;
                            CompareScanlineValue();
                        }

                        _lcdMode = LcdMode.OamRead;

                        // raise OAM stat interrupt if requested
                        if ((_lcdStatus & 0x20) == 0x20)
                        {
                            _interruptController.RequestInterrupt(Interrupt.LCDCStatus);
                        }
                    }
                }
                // Mode 3 - OAM and VRAM read, 162-175 cycles of frame
                else if ((_frameClock % 456) < 252)
                {
                    if (_lcdMode != LcdMode.VramRead)
                    {
                        _lcdMode = LcdMode.VramRead;
                    }
                }
                // Mode 0 - HBlank, 201-207 cycles of frame
                else
                {
                    if (_lcdMode != LcdMode.HBlank)
                    {
                        _lcdMode = LcdMode.HBlank;

                        // raise hblank stat interrupt if requested
                        if ((_lcdStatus & 0x08) == 0x08)
                        {
                            _interruptController.RequestInterrupt(Interrupt.LCDCStatus);
                        }

                        RenderScanline();
                    }
                }
            }
            // Mode 1 - VBlank
            else
            {
                // are we entering vblank from another mode?
                if (_lcdMode != LcdMode.VBlank)
                {
                    _lcdMode = LcdMode.VBlank;

                    _currentScanline++;
                    CompareScanlineValue();

                    // count 10 scanlines in vblank, include any already pushing into vblank timing
                    _vblankClock = _frameClock - 65664;

                    // raise vblank stat interrupt if requested
                    if ((_lcdStatus & 0x10) == 0x10)
                    {
                        _interruptController.RequestInterrupt(Interrupt.LCDCStatus);
                    }

                    // raise vblank interrupt
                    _interruptController.RequestInterrupt(Interrupt.VBlank);

                    //DEBUG_DumpVramTiles();

                    // we've just entered vblank so the rendering for the frame is finished
                    // present the screen data
                    _frameSink?.AppendFrame(_screenData);

                    // clear the _screenData for the next frame
                    Array.Clear(_screenData, 0, _screenData.Length);
                }
                // processing vblank
                else
                {
                    _vblankClock += cycleCount;

                    if (_vblankClock >= 456)
                    {
                        _vblankClock -= 456;
                        _currentScanline++;

                        if (_currentScanline > 153)
                        {
                            _frameClock     -= 70224;
                            _currentScanline = 0;
                        }

                        CompareScanlineValue();
                    }
                }
            }
        }
Ejemplo n.º 5
0
        public void WriteByte(ushort address, byte value)
        {
            SynchroniseWithSystemClock();

            // 0x8000 - 0x9FFF - vram
            if (address >= 0x8000 && address <= 0x9fff)
            {
                // cannot access vram in mode 3
                if (_lcdMode == LcdMode.VramRead)
                {
                    return;
                }

                _vram[address & 0x1FFF] = value;

                // invalidate the tile cache for any tiles updated
                if (address < 0x9800)
                {
                    _updateTileCache = true;
                    _tileCacheInvalid[(address & 0x1FF0) >> 4] = true;
                }
            }
            // 0xFE00 - 0xFE9F - OAM memory
            else if (address >= 0xFE00 && address <= 0xFE9F)
            {
                // cannot access oam in mode 2 or 3
                if (_lcdMode == LcdMode.OamRead || _lcdMode == LcdMode.VramRead || !_oamDma.OamAvailable)
                {
                    return;
                }

                _oam[address & 0xFF] = value;
            }
            else
            {
                switch (address)
                {
                case Registers.LCDC:
                    _lcdControl = value;

                    bool enableDisplay = (_lcdControl & 0x80) == 0x80;

                    // are we being asked to turn on the display and if so are we currently off?
                    if (enableDisplay && !_displayEnabled)
                    {
                        // lcd starts in mode 2 when enabled
                        _displayEnabled = true;
                        _lcdMode        = LcdMode.OamRead;

                        CompareScanlineValue();
                    }
                    // reset LY when display is disabled
                    else if (!enableDisplay && _displayEnabled)
                    {
                        _displayEnabled = false;

                        _currentScanline = 0;

                        _frameClock = 0;
                        _lcdMode    = 0;
                    }

                    _windowTileBaseAddress     = (ushort)((_lcdControl & 0x40) == 0x40 ? 0x9C00 : 0x9800);
                    _windowEnabled             = (_lcdControl & 0x20) == 0x20;
                    _backgroundCharBaseAddress = (ushort)((_lcdControl & 0x10) == 0x10 ? 0x8000 : 0x8800);

                    // if the background char data is in the range 0x8800 - 0x97FF then tile identifiers are signed
                    _signedCharIdentifier = _backgroundCharBaseAddress == 0x8800;

                    _backgroundTileBaseAddress = (ushort)((_lcdControl & 0x08) == 0x08 ? 0x9C00 : 0x9800);
                    _spriteHeight      = (_lcdControl & 0x04) == 0x04 ? 16 : 8;
                    _spritesEnabled    = (_lcdControl & 0x02) == 0x02;
                    _backgroundEnabled = (_lcdControl & 0x01) == 0x01;

                    break;

                case Registers.STAT:
                    // capture lcd interrupt selection from value
                    // interrupt selection stored in bits 6-3, preserve bits 0-2
                    // and mask bits 6-3 from value passed in
                    _lcdStatus = (byte)((_lcdStatus & 0x07) | (value & 0x78));

                    // a write to the LY = LYC match flag (bit 2) clears the flag
                    if ((value & 0x04) == 0x04)
                    {
                        unchecked { _lcdStatus &= (byte)~0x04; }
                    }

                    break;

                case Registers.SCY:
                    _scrollY = value;
                    break;

                case Registers.SCX:
                    _scrollX = value;
                    break;

                case Registers.LYC:
                    _scanlineCompare = value;
                    break;

                case Registers.DMA:
                    _oamDma.Source = (ushort)(value << 8);
                    _oamDma.Status = OamDmaStatus.Requested;

                    break;

                case Registers.BGP:
                    _bgp = value;

                    _backgroundPalette = ExtractPaletteData(_bgp, 0);
                    break;

                case Registers.OBP0:
                    _obp0 = value;

                    // extract sprite palette data, start from index 1, palette entry 00 is always 00
                    _spritePalette[0] = ExtractPaletteData(_obp0, 1);
                    break;

                case Registers.OBP1:
                    _obp1 = value;

                    // extract sprite palette data, start from index 1, palette entry 00 is always 00
                    _spritePalette[1] = ExtractPaletteData(_obp1, 1);
                    break;

                case Registers.WY:
                    _windowY = value;
                    break;

                case Registers.WX:
                    _windowX = value;
                    break;

                default:
                    throw new ArgumentOutOfRangeException("address");
                }
            }
        }