Ejemplo n.º 1
0
Archivo: PPU.cs Proyecto: mrmikey/nes
        public PPU(Engine engine)
        {
            this.Engine = engine;
            this.dumpSprite = new SpriteDumpFunc(dumpSpriteSmall);
            // Attribute Lookup Tables
             	for (int i = 0; i != 0x400; ++i)
            {
                AttributeShiftTable[i] = (byte)(((i >> 4) & 0x04) | (i & 0x02));
                AttributeLocationTable[i] = (byte)(((i >> 4) & 0x38) | ((i >> 2))  & 7);
               }

            // Setup nametables
            Nametables.SetupNametables(this.Mirroring);
        }
Ejemplo n.º 2
0
        public void Run(int cpuCycles)
        {
            int ppuCycles = (int)((decimal)(cpuCycles + CycleCarry) * CPUScaling); // Number of ppu cycles to do
            int endCycle  = 341;                                                   // 0-340 cycles inclusive for 341 cycles total.

            // Timing!
            for (int i = 0; i < ppuCycles; ++i)
            {
                //	if ((Cycle == 0) && (CurrentScaline == 0))
                //		Flags.Loopy_V = (ushort)(0x2000 | (Flags.NameTableAddress << 10));

                ++Cycle;
                if (Cycle == 256)                 // Significance?
                {
                    // Todo: handle short scanlines, see nes_emu.txt
                    endCycle = 341;
                }
                else if (Cycle == 304)
                {
                    //frame start, $2006 gets reloaded with the tmp addr
                    //this happens in the dummy scanline, and the PPU
                    //is rendering. The reason for the reload because
                    //Loopy_V is changed as the PPU is rendering.
                    //Loopy_V is the "program counter" for the PPU.
                    if ((CurrentScaline < 0) && Rendering)
                    {
                        Flags.Loopy_V = Flags.Loopy_T;
                    }
                }
                else if (Cycle == endCycle)
                {
                    ++CurrentScaline;
                    Cycle = 0;

                    // Start of VBlank (240 is idle)
                    if (CurrentScaline == 241)
                    {
                        //dumpNametable();
                        //dumpSprites();
                        Engine.Graphics.Render();
                        Flags.Status  |= 0x80;                                         // Set VBlank flag in $2002
                        Engine.CPU.NMI = ((Flags.Control1 & 0x80) > 1) ? true : false; // Only trigger NMI if they want us to in $2000
                        Flags.SprAddr  = 0;                                            // Gets reset at end of frame
                        Engine.CPU.Flags.InterruptDisable = true;                      // Set NMI
                        Engine.CPU.SinceLastVBlank        = 0;
                    }
                    else if (CurrentScaline == EndScanline)
                    {
                        CurrentScaline = -1;
                        CurrentSprite  = 0;
                        if (Flags.TallSprites)
                        {
                            dumpSprite = new SpriteDumpFunc(dumpSpriteTall);
                        }
                        else
                        {
                            dumpSprite = new SpriteDumpFunc(dumpSpriteSmall);
                        }
                        // todo: this.Rendering, and short scanlines
                    }
                }
                else if ((CurrentScaline < 0) && (Cycle == 1))
                {
                    // VBlank gets cleared at cycle 1 of scanline -1
                    Flags.Status = 0;                     // ALL 0?!
                }

                // Sprites!
                if ((CurrentSprite < 32) && (CurrentScaline > 0))
                {
                    dumpSprite(CurrentSprite * 4);
                    CurrentSprite++;
                }

                // :D
                if ((CurrentScaline < 240) && (CurrentScaline > 0))
                {
                    // Datas!
                    switch (Cycle)
                    {
                    // Cycle 0 - Get tile
                    // Cycle 1 - Gets the pattern address for the tile
                    // Cycle 2 - Gets attribute addr[/shift]
                    // Cycle 3 - Apply attribute
                    // Cycle 4 - Nothing
                    // Cycle 5 - Get lower bit from nt [We do upper too]
                    // Cycle 6 - Get pattern address for upper two bits [We don't need this]
                    // Cycle 7 - Get upper bit from nt [Did this in Cycle 5]
                    // Rinse and repeat.
                    // Buuut we're not gonna do that -- Just make sure we update Loopy_V at cycle 251

                    // Cycle 0 - We'll do all of the above! :)
                    case   0:
                    case   8:
                    case  16:
                    case  24:
                    case  32:
                    case  40:
                    case  48:
                    case  56:
                    case  64:
                    case  72:
                    case  80:
                    case  88:
                    case  96:
                    case 104:
                    case 112:
                    case 120:
                    case 128:
                    case 136:
                    case 144:
                    case 152:
                    case 160:
                    case 168:
                    case 176:
                    case 184:
                    case 192:
                    case 200:
                    case 208:
                    case 216:
                    case 224:
                    case 232:
                    case 240:
                    case 248:
                        // Find out which pattern to use
                        byte tile = Nametables[Flags.Loopy_V];

                        int pattern_addr = (tile << 6) | (Flags.BGTable << 14);

                        // Get attribute
                        int attribute_addr  = 0x23C0 | (Flags.Loopy_V & 0xC00) | (AttributeLocationTable[Flags.Loopy_V & 0x3FF]);
                        int attribute_shift = AttributeShiftTable[Flags.Loopy_V & 0x3FF];
                        int attribute       = ((Nametables[attribute_addr] >> attribute_shift) & 3) << 2;                           // Remember attribute is upper 2 bits of color.

                        // Now we have the pattern_addr and the attribute, we can render! :)
                        int x = Flags.Loopy_V & 31;                                 // bits 0-4 are x-scroll
                        int y = (Flags.Loopy_V & 0x3E0) >> 5;                       // bits 5-9 are y-scroll.
                        y = (y > 29) ? 29 : y;
                        Engine.Graphics.DrawTile(Engine.Cartridge.CHRBanks[0], Engine.Graphics.BGBuffer, 0, (256 * Flags.BGTable) + tile, x * 8, y * 8, attribute);


                        break;

                    case   3:
                    case  11:
                    case  19:
                    case  27:
                    case  35:
                    case  43:
                    case  51:
                    case  59:
                    case  67:
                    case  75:
                    case  83:
                    case  91:
                    case  99:
                    case 107:
                    case 115:
                    case 123:
                    case 131:
                    case 139:
                    case 147:
                    case 155:
                    case 163:
                    case 171:
                    case 179:
                    case 187:
                    case 195:
                    case 203:
                    case 211:
                    case 219:
                    case 227:
                    case 235:
                    case 243:

                        if ((Flags.Loopy_V & 0x1F) == 0x1F)
                        {
                            Flags.Loopy_V ^= 0x41F;
                        }
                        else
                        {
                            ++Flags.Loopy_V;
                        }

                        break;

                    case 323:
                    case 331:

                        if ((Flags.Loopy_V & 0x1F) == 0x1F)
                        {
                            Flags.Loopy_V ^= 0x41F;
                        }
                        else
                        {
                            ++Flags.Loopy_V;
                        }


                        break;


                    case 251:

                        if ((Flags.Loopy_V & 0x1F) == 0x1F)
                        {
                            Flags.Loopy_V ^= 0x41F;
                        }
                        else
                        {
                            ++Flags.Loopy_V;
                        }



                        if ((Flags.Loopy_V & 0x7000) == 0x7000)
                        {
                            int tmp = Flags.Loopy_V & 0x3E0;
                            //reset tile y offset 12 - 14 in addr
                            Flags.Loopy_V &= 0xFFF;
                            switch (tmp)
                            {
                            //29, flip bit 11
                            case 0x3A0:
                                Flags.Loopy_V ^= 0xBA0;
                                break;

                            case 0x3E0:                                             //31, back to 0
                                Flags.Loopy_V ^= 0x3E0;
                                break;

                            default:                                             //inc y scroll if not reached
                                Flags.Loopy_V += 0x20;
                                break;
                            }
                        }
                        else                                 //inc fine y
                        {
                            Flags.Loopy_V += 0x1000;
                        }

                        break;
                    }
                }
            }
        }
Ejemplo n.º 3
0
Archivo: PPU.cs Proyecto: mrmikey/nes
        public void Run(int cpuCycles)
        {
            int ppuCycles = (int)((decimal)(cpuCycles + CycleCarry) * CPUScaling); // Number of ppu cycles to do
            int endCycle = 341; // 0-340 cycles inclusive for 341 cycles total.

            // Timing!
            for (int i = 0; i < ppuCycles; ++i)
            {
            //	if ((Cycle == 0) && (CurrentScaline == 0))
            //		Flags.Loopy_V = (ushort)(0x2000 | (Flags.NameTableAddress << 10));

                ++Cycle;
                if (Cycle == 256) // Significance?
                {
                    // Todo: handle short scanlines, see nes_emu.txt
                    endCycle = 341;
                } else if (Cycle == 304)
                {
                    //frame start, $2006 gets reloaded with the tmp addr
                    //this happens in the dummy scanline, and the PPU
                    //is rendering. The reason for the reload because
                    //Loopy_V is changed as the PPU is rendering.
                    //Loopy_V is the "program counter" for the PPU.
                    if ((CurrentScaline < 0) && Rendering)
                        Flags.Loopy_V = Flags.Loopy_T;
                } else if (Cycle == endCycle)
                {
                    ++CurrentScaline;
                    Cycle = 0;

                    // Start of VBlank (240 is idle)
                    if (CurrentScaline == 241)
                    {
                        //dumpNametable();
                        //dumpSprites();
                        Engine.Graphics.Render();
                        Flags.Status |= 0x80; // Set VBlank flag in $2002
                        Engine.CPU.NMI = ((Flags.Control1 & 0x80) > 1) ? true : false; // Only trigger NMI if they want us to in $2000
                        Flags.SprAddr = 0; // Gets reset at end of frame
                        Engine.CPU.Flags.InterruptDisable = true; // Set NMI
                        Engine.CPU.SinceLastVBlank = 0;
                    } else if (CurrentScaline == EndScanline)
                    {
                        CurrentScaline = -1;
                        CurrentSprite = 0;
                        if (Flags.TallSprites)
                            dumpSprite = new SpriteDumpFunc(dumpSpriteTall);
                        else
                            dumpSprite = new SpriteDumpFunc(dumpSpriteSmall);
                        // todo: this.Rendering, and short scanlines
                    }
                } else if ((CurrentScaline < 0) && (Cycle == 1))
                {
                    // VBlank gets cleared at cycle 1 of scanline -1
                    Flags.Status = 0; // ALL 0?!
                }

                // Sprites!
                if ((CurrentSprite < 32) && (CurrentScaline > 0))
                {
                    dumpSprite(CurrentSprite * 4);
                    CurrentSprite++;
                }

                // :D
                if ((CurrentScaline < 240) && (CurrentScaline > 0))
                {
                    // Datas!
                    switch(Cycle)
                    {
                        // Cycle 0 - Get tile
                        // Cycle 1 - Gets the pattern address for the tile
                        // Cycle 2 - Gets attribute addr[/shift]
                        // Cycle 3 - Apply attribute
                        // Cycle 4 - Nothing
                        // Cycle 5 - Get lower bit from nt [We do upper too]
                        // Cycle 6 - Get pattern address for upper two bits [We don't need this]
                        // Cycle 7 - Get upper bit from nt [Did this in Cycle 5]
                        // Rinse and repeat.
                        // Buuut we're not gonna do that -- Just make sure we update Loopy_V at cycle 251

                        // Cycle 0 - We'll do all of the above! :)
                        case   0:	case   8:	case  16:	case  24:
                        case  32:	case  40:	case  48:	case  56:
                        case  64:	case  72:	case  80:	case  88:
                        case  96:	case 104:	case 112:	case 120:
                        case 128:	case 136:	case 144:	case 152:
                        case 160:	case 168:	case 176:	case 184:
                        case 192:	case 200:	case 208:	case 216:
                        case 224:	case 232:	case 240:	case 248:
                            // Find out which pattern to use
                            byte tile = Nametables[Flags.Loopy_V];

                            int pattern_addr = (tile << 6) | (Flags.BGTable << 14);

                            // Get attribute
                            int attribute_addr = 0x23C0 | (Flags.Loopy_V & 0xC00) | (AttributeLocationTable[Flags.Loopy_V & 0x3FF]);
                            int attribute_shift = AttributeShiftTable[Flags.Loopy_V & 0x3FF];
                            int attribute = ((Nametables[attribute_addr] >> attribute_shift) & 3) << 2; // Remember attribute is upper 2 bits of color.

                            // Now we have the pattern_addr and the attribute, we can render! :)
                            int x = Flags.Loopy_V & 31; // bits 0-4 are x-scroll
                            int y = (Flags.Loopy_V & 0x3E0) >> 5; // bits 5-9 are y-scroll.
                            y = (y > 29) ? 29 : y;
                            Engine.Graphics.DrawTile(Engine.Cartridge.CHRBanks[0], Engine.Graphics.BGBuffer, 0, (256 * Flags.BGTable) + tile, x*8, y*8, attribute);

                            break;

                        case   3:	case  11:	case  19:	case  27:
                        case  35:	case  43:	case  51:	case  59:
                        case  67:	case  75:	case  83:	case  91:
                        case  99:	case 107:	case 115:	case 123:
                        case 131:	case 139:	case 147:	case 155:
                        case 163:	case 171:	case 179:	case 187:
                        case 195:	case 203:	case 211:	case 219:
                        case 227:	case 235:	case 243:

                            if ((Flags.Loopy_V & 0x1F) == 0x1F)
                             	 Flags.Loopy_V ^= 0x41F;
                            else
                                ++Flags.Loopy_V;

                            break;

                        case 323:	case 331:

                            if ((Flags.Loopy_V & 0x1F) == 0x1F)
                             	 Flags.Loopy_V ^= 0x41F;
                            else
                                ++Flags.Loopy_V;

                            break;

                        case 251:

                            if ((Flags.Loopy_V & 0x1F) == 0x1F)
                             	 Flags.Loopy_V ^= 0x41F;
                            else
                                ++Flags.Loopy_V;

                            if ((Flags.Loopy_V & 0x7000) == 0x7000)
                            {
                                int tmp = Flags.Loopy_V & 0x3E0;
                                //reset tile y offset 12 - 14 in addr
                                Flags.Loopy_V &= 0xFFF;
                                switch (tmp)
                                {
                                    //29, flip bit 11
                                    case 0x3A0:
                                        Flags.Loopy_V ^= 0xBA0;
                                        break;
                                    case 0x3E0: //31, back to 0
                                        Flags.Loopy_V ^= 0x3E0;
                                        break;
                                    default: //inc y scroll if not reached
                                        Flags.Loopy_V += 0x20;
                                        break;
                                }
                            }
                            else //inc fine y
                                Flags.Loopy_V += 0x1000;

                            break;
                    }
                }
            }
        }