예제 #1
0
 public PPU(Cpu cpu, Cartridge cartridge, IDisplay display, Nes nes)
 {
     _nes       = nes;
     _display   = display;
     _cpu       = cpu;
     _cartridge = cartridge;
     for (int i = 0; i < 64; i++)
     {
         Oam[i] = new Oam();
     }
 }
예제 #2
0
        public void Clock()
        {
            if (_scanline >= -1 && _scanline < 240)
            {
                if (_scanline == 0 && _cycle == 0)
                {
                    _cycle = 1;
                }
                else if (_scanline == -1 && _cycle == 1)
                {
                    PPURegisters.PPUSTATUS.VerticalBlank = false;
                    // Clear Shifters
                    for (int i = 0; i < 8; i++)
                    {
                        _spriteShifterPatternLow[i]           = 0;
                        _spriteShifterPatternHigh[i]          = 0;
                        PPURegisters.PPUSTATUS.SpriteOverflow = false;
                        PPURegisters.PPUSTATUS.SpriteHit      = false;
                    }
                }
                else if ((_cycle >= 2 && _cycle < 258) || (_cycle >= 321 && _cycle < 338))
                {
                    UpdateShifters();

                    switch ((_cycle - 1) % 8)
                    {
                    case 0:
                        LoadBgShifters();
                        _bgNextTileId = ReadPpu(NAMETABLE | (_vram.Value & 0x0FFF));
                        break;

                    case 2:
                        _bgNextTileAttr = ReadPpu(NAMETABLE_ATTRIBUTES | (_vram.NametableY << 11)
                                                  | (_vram.NametableX << 10)
                                                  | ((_vram.CoarseY >> 2) << 3)
                                                  | (_vram.CoarseX >> 2));

                        if ((_vram.CoarseY & 0x02) != 0)
                        {
                            _bgNextTileAttr >>= 4;
                        }
                        if ((_vram.CoarseX & 0x02) != 0)
                        {
                            _bgNextTileAttr >>= 2;
                        }

                        _bgNextTileAttr &= 0x03;
                        break;

                    case 4:
                        _bgNextTileLo = ReadPpu(((PPURegisters.PPUCTRL.PatternBackground ? 1 : 0) << 12)
                                                + (_bgNextTileId << 4) + _vram.FineY);
                        break;

                    case 6:
                        _bgNextTileHi = ReadPpu(((PPURegisters.PPUCTRL.PatternBackground ? 1 : 0) << 12)
                                                + (_bgNextTileId << 4) + (_vram.FineY) + 8);
                        break;

                    case 7:
                        IncrementScrollX();
                        break;

                    default:
                        break;
                    }
                }

                if (_cycle == 256)
                {
                    IncrementScrollY();
                }
                else if (_cycle == 257)
                {
                    LoadBgShifters();
                    TransferAddressX();
                }
                else if (_cycle == 338 || _cycle == 340)
                {
                    _bgNextTileId = ReadPpu(NAMETABLE | (_vram.Value & 0x0FFF));
                }
                else if (_scanline == -1 && _cycle >= 280 && _cycle < 305)
                {
                    TransferAddressY();
                }
                if (_cycle == 257 && _scanline >= 0)
                {
                    for (int i = 0; i < 8; i++)
                    {
                        _spriteScanline[i] = new Oam();
                    }
                    _spriteCount = 0;

                    for (int i = 0; i < 8; i++)
                    {
                        _spriteShifterPatternLow[i]  = 0;
                        _spriteShifterPatternHigh[i] = 0;
                    }

                    byte index = 0;

                    _spriteZeroHitPossible = false;

                    while (index < 64 && _spriteCount < 9)
                    {
                        int diff = _scanline - Oam[index].Y;

                        if (diff >= 0 && diff < (PPURegisters.PPUCTRL.SpriteHeight ? 16 : 8))
                        {
                            if (_spriteCount < 8)
                            {
                                if (index == 0)
                                {
                                    _spriteZeroHitPossible = true;
                                }

                                _spriteScanline[_spriteCount] = Oam[index];
                                _spriteCount++;
                            }
                        }

                        index++;
                    }
                    PPURegisters.PPUSTATUS.SpriteOverflow = (_spriteCount > 8);
                }
                if (_cycle == 340)
                {
                    for (byte i = 0; i < _spriteCount; i++)
                    {
                        byte   spritePatternBitsLo, spritePatternBitsHi;
                        ushort spritePatternAddrLo, spritePatternAddrHi;

                        if (!PPURegisters.PPUCTRL.SpriteHeight)
                        {
                            if ((_spriteScanline[i].Attributes & 0x80) == 0)
                            {
                                spritePatternAddrLo = (ushort)(
                                    ((PPURegisters.PPUCTRL.PatternSprite ? 1 : 0) << 12)
                                    | (_spriteScanline[i].Id << 4)
                                    | (_scanline - _spriteScanline[i].Y));
                            }
                            else
                            {
                                spritePatternAddrLo = (ushort)(
                                    ((PPURegisters.PPUCTRL.PatternSprite ? 1 : 0) << 12)
                                    | (_spriteScanline[i].Id << 4)
                                    | (7 - (_scanline - _spriteScanline[i].Y)));
                            }
                        }
                        else
                        {
                            if ((_spriteScanline[i].Attributes & 0x80) == 0)
                            {
                                if (_scanline - _spriteScanline[i].Y < 8)
                                {
                                    spritePatternAddrLo = (ushort)(
                                        ((_spriteScanline[i].Id & 0x01) << 12)
                                        | ((_spriteScanline[i].Id & 0xFE) << 4)
                                        | ((_scanline - _spriteScanline[i].Y) & 0x07));
                                }
                                else
                                {
                                    spritePatternAddrLo = (ushort)(
                                        ((_spriteScanline[i].Id & 0x01) << 12)
                                        | (((_spriteScanline[i].Id & 0xFE) + 1) << 4)
                                        | ((_scanline - _spriteScanline[i].Y) & 0x07));
                                }
                            }
                            else
                            {
                                if (_scanline - _spriteScanline[i].Y < 8)
                                {
                                    spritePatternAddrLo = (ushort)(
                                        ((_spriteScanline[i].Id & 0x01) << 12)
                                        | (((_spriteScanline[i].Id & 0xFE) + 1) << 4)
                                        | (7 - (_scanline - _spriteScanline[i].Y) & 0x07));
                                }
                                else
                                {
                                    spritePatternAddrLo = (ushort)(((_spriteScanline[i].Id & 0x01) << 12)
                                                                   | ((_spriteScanline[i].Id & 0xFE) << 4)
                                                                   | (7 - (_scanline - _spriteScanline[i].Y) & 0x07));
                                }
                            }
                        }

                        spritePatternAddrHi = (ushort)(spritePatternAddrLo + 8);

                        spritePatternBitsLo = ReadPpu(spritePatternAddrLo);
                        spritePatternBitsHi = ReadPpu(spritePatternAddrHi);

                        if ((_spriteScanline[i].Attributes & 0x40) > 0)
                        {
                            spritePatternBitsLo = FlipByte(spritePatternBitsLo);
                            spritePatternBitsHi = FlipByte(spritePatternBitsHi);
                        }

                        _spriteShifterPatternLow[i]  = spritePatternBitsLo;
                        _spriteShifterPatternHigh[i] = spritePatternBitsHi;
                    }
                }
            }
            else if (_scanline == 240)
            {
                //do nothing
            }
            else if (_scanline == 241 && _cycle == 1)
            {
                //First pixel of the nonvisible scanline
                PPURegisters.PPUSTATUS.VerticalBlank = true;
                if (PPURegisters.PPUCTRL.NmiEnabled)
                {
                    _cpu.Nmi();
                }
            }

            if (_cycle > 0 && _scanline >= 0 && _cycle - 1 < 256 && _scanline < 240)
            {
                byte bg_pixel   = 0x00;
                byte bg_palette = 0x00;

                if (PPURegisters.PPUMASK.BackgroundEnable)
                {
                    var bit_mux = (ushort)(0x8000 >> _fineX);

                    var p0_pixel = (_bgShifterPatternLo & bit_mux) != 0 ? 1 : 0;
                    var p1_pixel = (_bgShifterPatternHi & bit_mux) != 0 ? 1 : 0;

                    bg_pixel = (byte)((p1_pixel << 1) | p0_pixel);

                    var bg_pal0 = (_bgShifterAttributeLo & bit_mux) != 0 ? 1 : 0;
                    var bg_pal1 = (_bgShifterAttributeHi & bit_mux) != 0 ? 1 : 0;
                    bg_palette = (byte)((bg_pal1 << 1) | bg_pal0);
                }

                byte fg_pixel    = 0x00;
                byte fg_palette  = 0x00;
                byte fg_priority = 0x00;


                if (PPURegisters.PPUMASK.SpriteEnable)
                {
                    _spriteZeroBeingRendered = false;

                    for (byte i = 0; i < _spriteCount; i++)
                    {
                        if (_spriteScanline[i].X == 0)
                        {
                            byte fg_pixel_lo = (byte)((_spriteShifterPatternLow[i] & 0x80) > 0 ? 1 : 0);
                            byte fg_pixel_hi = (byte)((_spriteShifterPatternHigh[i] & 0x80) > 0 ? 1 : 0);
                            fg_pixel    = (byte)((fg_pixel_hi << 1) | fg_pixel_lo);
                            fg_palette  = (byte)((_spriteScanline[i].Attributes & 0x03) + 0x04);
                            fg_priority = (byte)((_spriteScanline[i].Attributes & 0x20) == 0 ? 1 : 0);

                            if (fg_pixel != 0)
                            {
                                if (i == 0)
                                {
                                    _spriteZeroBeingRendered = true;
                                }
                                break;
                            }
                        }
                    }
                }

                byte pixel   = 0x00;
                byte palette = 0x00;

                if (bg_pixel == 0 && fg_pixel == 0)
                {
                    pixel   = 0x00;
                    palette = 0x00;
                }
                else if (bg_pixel == 0 && fg_pixel > 0)
                {
                    pixel   = fg_pixel;
                    palette = fg_palette;
                }
                else if (bg_pixel > 0 && fg_pixel == 0)
                {
                    pixel   = bg_pixel;
                    palette = bg_palette;
                }
                else if (bg_pixel > 0 && fg_pixel > 0)
                {
                    if (fg_priority == 1)
                    {
                        pixel   = fg_pixel;
                        palette = fg_palette;
                    }
                    else
                    {
                        pixel   = bg_pixel;
                        palette = bg_palette;
                    }

                    if (_spriteZeroHitPossible && _spriteZeroBeingRendered)
                    {
                        if (PPURegisters.PPUMASK.SpriteEnable && PPURegisters.PPUMASK.BackgroundEnable)
                        {
                            if (!(PPURegisters.PPUMASK.BackgroundLeftColumnEnable || PPURegisters.PPUMASK.SpriteLeftColumnEnable))
                            {
                                if (_cycle >= 9 && _cycle < 258)
                                {
                                    PPURegisters.PPUSTATUS.SpriteHit = true;
                                }
                            }
                            else
                            {
                                if (_cycle >= 1 && _cycle < 258)
                                {
                                    PPURegisters.PPUSTATUS.SpriteHit = true;
                                }
                            }
                        }
                    }
                }

                _display.DrawPixel(_cycle - 1, _scanline, GetColorFromPalette(palette, pixel));
                if (_nes.SuperSlow > 0 && _cycle > 2 && _cycle < 256)
                {
                    _display.DrawPixel((_cycle - 1 + 1) % 256, _scanline, 0xFF00fF);
                }
            }

            _cycle++;
            if (_cycle >= 341)
            {
                _cycle = 0;
                _scanline++;
                if (_scanline >= 261)
                {
                    _scanline     = -1;
                    FrameFinished = true;
                    _display.SetFrameFinished();
                }
            }
        }