Beispiel #1
0
        private static void PPUClock()
        {
            board.OnPPUClock();

            if ((VClock < 240) || (VClock == vbl_vclock_End))
            {
                if (bkg_enabled || spr_enabled)
                {
                    if (HClock < 256)
                    {
                        #region BKG FETCHES 0 - 255
                        // UNUSED AT 248-255
                        switch (HClock & 7)
                        {
                        case 0:
                        {
                            // Fetch address of nametable
                            bkg_fetch_address = 0x2000 | (vram_address & 0x0FFF);
                            board.OnPPUAddressUpdate(ref bkg_fetch_address);
                            break;
                        }

                        case 1: bkg_fetch_nametable = board.ReadNMT(ref bkg_fetch_address); break;

                        case 2:
                        {
                            // Fetch address for attr byte
                            bkg_fetch_address = 0x23C0 | (vram_address & 0xC00) | (vram_address >> 4 & 0x38) | (vram_address >> 2 & 0x7);
                            board.OnPPUAddressUpdate(ref bkg_fetch_address);
                            break;
                        }

                        case 3: bkg_fetch_attr = (byte)(board.ReadNMT(ref bkg_fetch_address) >> ((vram_address >> 4 & 0x04) | (vram_address & 0x02))); break;

                        case 4:
                        {
                            // Fetch bit 0 address
                            bkg_fetch_address = bkg_patternAddress | (bkg_fetch_nametable << 4) | 0 | (vram_address >> 12 & 7);
                            board.OnPPUAddressUpdate(ref bkg_fetch_address);
                            break;
                        }

                        case 5: bkg_fetch_bit0 = board.ReadCHR(ref bkg_fetch_address, false); break;

                        case 6:
                        {
                            // Fetch bit 1 address
                            bkg_fetch_address = bkg_patternAddress | (bkg_fetch_nametable << 4) | 8 | (vram_address >> 12 & 7);
                            board.OnPPUAddressUpdate(ref bkg_fetch_address);
                            break;
                        }

                        case 7:
                        {
                            bkg_fetch_bit1 = board.ReadCHR(ref bkg_fetch_address, false);
                            if (HClock == 255)
                            {
                                // Increment Y
                                if ((vram_address & 0x7000) != 0x7000)
                                {
                                    vram_address += 0x1000;
                                }
                                else
                                {
                                    vram_address ^= 0x7000;

                                    switch (vram_address & 0x3E0)
                                    {
                                    case 0x3A0: vram_address ^= 0xBA0; break;

                                    case 0x3E0: vram_address ^= 0x3E0; break;

                                    default: vram_address += 0x20; break;
                                    }
                                }
                            }
                            else
                            {
                                // Increment X
                                if ((vram_address & 0x001F) == 0x001F)
                                {
                                    vram_address ^= 0x041F;
                                }
                                else
                                {
                                    vram_address++;
                                }
                            }
                            // Render BKG tile
                            bkg_pos = (HClock + 9) % 336;

                            for (bkg_render_i = 0; bkg_render_i < 8 && bkg_pos < 272; bkg_render_i++, bkg_pos++, bkg_fetch_bit0 <<= 1, bkg_fetch_bit1 <<= 1)
                            {
                                bkg_pixels[bkg_pos] = (bkg_fetch_attr << 2 & 12) | (bkg_fetch_bit0 >> 7 & 1) | (bkg_fetch_bit1 >> 6 & 2);
                            }

                            break;
                        }
                        }
                        #endregion
                        #region Render Pixel
                        if (VClock < 240)
                        {
                            if (!bkg_enabled || (bkg_clipped && HClock < 8))
                            {
                                bkgPixel = 0x3F00;
                            }
                            else
                            {
                                bkgPixel = 0x3F00 | bkg_pixels[HClock + vram_fine];
                            }
                            if (!spr_enabled || (spr_clipped && HClock < 8))
                            {
                                sprPixel = 0x3F10;
                            }
                            else
                            {
                                sprPixel = 0x3F10 | spr_pixels[HClock];
                            }

                            current_pixel = 0;

                            //Priority *******************************
                            if ((sprPixel & 0x8000) != 0)
                            {
                                current_pixel = sprPixel;
                            }
                            else
                            {
                                current_pixel = bkgPixel;
                            }
                            //****************************************

                            // Transparency **************************
                            if ((bkgPixel & 0x03) == 0)
                            {
                                current_pixel = sprPixel;
                                goto render;
                            }

                            if ((sprPixel & 0x03) == 0)
                            {
                                current_pixel = bkgPixel;
                                goto render;
                            }
                            //****************************************

                            //Sprite 0 Hit
                            if ((sprPixel & 0x4000) != 0 & HClock < 255)
                            {
                                spr_0Hit = true;
                            }

render:
                            if (!screenPointerMode)
                            {
                                screen[(VClock * 256) + HClock] = palette[paletteIndexes[palettes_bank[current_pixel & ((current_pixel & 0x03) == 0 ? 0x0C : 0x1F)]
                                                                                         & (grayscale | emphasis)]];
                            }
                            else
                            {
                                screenPointerPos = (VClock * 256) + HClock;
                                if (screenPointerPos >= screenPointerStart && screenPointerPos < screenPointerSize)
                                {
                                    screenPointer[screenPointerPos - screenPointerStart] = palette[paletteIndexes[palettes_bank[current_pixel & ((current_pixel & 0x03) == 0 ? 0x0C : 0x1F)]
                                                                                                                  & (grayscale | emphasis)]];
                                }
                            }
                        }
                        #endregion
                        #region OAM EVALUATION
                        switch (HClock & 1)
                        {
                        case 0:
                        {
                            if (!oam_fetch_mode)
                            {
                                oam_fetch_data = 0xFF;
                            }
                            else
                            {
                                oam_fetch_data = oam_ram[oam_address];
                            }
                            break;
                        }

                        case 1:
                        {
                            switch (oam_phase_index)
                            {
                            case 0:
                            {
                                if (HClock <= 64)
                                {
                                    switch (HClock >> 1 & 0x03)
                                    {
                                    case 0: oam_secondary[((HClock >> 3) * 4) + 0] = 0xFF; break;

                                    case 1: oam_secondary[((HClock >> 3) * 4) + 1] = 0xFF; break;

                                    case 2: oam_secondary[((HClock >> 3) * 4) + 2] = 0xFF; break;

                                    case 3:
                                    {
                                        oam_secondary[((HClock >> 3) * 4) + 3] = 0xFF;
                                        spr_zero_buffer[HClock >> 3 & 7]       = false;
                                        break;
                                    }
                                    }
                                }
                                break;
                            }

                            case 1:
                            {
                                if (VClock == vbl_vclock_End)
                                {
                                    break;
                                }
                                oam_evaluate_count++;
                                temp_comparator = (VClock - oam_fetch_data) & int.MaxValue;

                                if (temp_comparator >= spr_size16)
                                {
                                    if (oam_evaluate_count != 64)
                                    {
                                        oam_address = (byte)(oam_evaluate_count != 2 ? oam_address + 4 : 8);
                                    }
                                    else
                                    {
                                        oam_address     = 0;
                                        oam_phase_index = 9;
                                    }
                                }
                                else
                                {
                                    oam_address++;
                                    oam_phase_index = 2;
                                    oam_secondary[oam_evaluate_slot * 4] = oam_fetch_data;

                                    spr_zero_buffer[oam_evaluate_slot] = (oam_evaluate_count == 1);
                                }
                                break;
                            }

                            case 2:
                            {
                                oam_address++;
                                oam_phase_index = 3;
                                oam_secondary[(oam_evaluate_slot * 4) + 1] = oam_fetch_data;
                                break;
                            }

                            case 3:
                            {
                                oam_address++;
                                oam_phase_index = 4;
                                oam_secondary[(oam_evaluate_slot * 4) + 2] = oam_fetch_data;
                                break;
                            }

                            case 4:
                            {
                                oam_secondary[(oam_evaluate_slot * 4) + 3] = oam_fetch_data;
                                oam_evaluate_slot++;

                                if (oam_evaluate_count != 64)
                                {
                                    oam_phase_index = (byte)((oam_evaluate_slot != 8) ? 1 : 5);
                                    if (oam_evaluate_count != 2)
                                    {
                                        oam_address++;
                                    }
                                    else
                                    {
                                        oam_address = 8;
                                    }
                                }
                                else
                                {
                                    oam_address     = 0;
                                    oam_phase_index = 9;
                                }
                                break;
                            }

                            case 5:
                            {
                                if (VClock == vbl_vclock_End)
                                {
                                    break;
                                }
                                temp_comparator = (VClock - oam_fetch_data) & int.MaxValue;

                                if (temp_comparator >= spr_size16)
                                {
                                    oam_address = (byte)(((oam_address + 4) & 0xFC) +
                                                         ((oam_address + 1) & 0x03));

                                    if (oam_address <= 5)
                                    {
                                        oam_phase_index = 9;
                                        oam_address    &= 0xFC;
                                    }
                                }
                                else
                                {
                                    oam_phase_index = 6;
                                    oam_address    += (0x01) & 0xFF;
                                    spr_overflow    = true;
                                }
                                break;
                            }

                            case 6:
                            {
                                oam_phase_index = 7;
                                oam_address    += (0x01);
                                break;
                            }

                            case 7:
                            {
                                oam_phase_index = 8;
                                oam_address    += (0x01);
                                break;
                            }

                            case 8:
                            {
                                oam_phase_index = 9;
                                oam_address    += (0x01);

                                if ((oam_address & 0x03) == 0x03)
                                {
                                    oam_address += (0x01);
                                }

                                oam_address &= 0xFC;
                                break;
                            }

                            case 9:
                            {
                                oam_address += 0x4;
                                break;
                            }
                            }
                            break;
                        }
                        }
                        if (HClock == 63)
                        {//Evaluation Begin
                            oam_fetch_mode     = true;
                            oam_phase_index    = 1;
                            oam_evaluate_slot  = 0;
                            oam_evaluate_count = 0;
                        }

                        if (HClock == 255)
                        {
                            // Evaluation Reset
                            oam_fetch_mode     = false;
                            oam_phase_index    = 0;
                            oam_evaluate_slot  = 0;
                            oam_address        = 0;
                            oam_evaluate_count = 0;

                            //spr_pixels = new int[256];
                            for (spr_evaluation_i = 0; spr_evaluation_i < 256; spr_evaluation_i++)
                            {
                                spr_pixels[spr_evaluation_i] = 0;
                            }
                        }
                        #endregion
                    }
                    else if (HClock < 320)
                    {
                        #region SPRITE FETCHES + GARBAGE BKG FETCHES 256 - 319
                        switch (HClock & 7)
                        {
                        case 0:
                        {
                            // Fetch address of nametable
                            bkg_fetch_address = 0x2000 | (vram_address & 0x0FFF);
                            board.OnPPUAddressUpdate(ref bkg_fetch_address);
                            break;
                        }

                        case 1: bkg_fetch_nametable = board.ReadNMT(ref bkg_fetch_address); break;

                        case 2:
                        {
                            // Fetch address for attr byte
                            bkg_fetch_address = 0x23C0 | (vram_address & 0xC00) | (vram_address >> 4 & 0x38) | (vram_address >> 2 & 0x7);
                            board.OnPPUAddressUpdate(ref bkg_fetch_address);
                            break;
                        }

                        case 3: bkg_fetch_attr = (byte)(board.ReadNMT(ref bkg_fetch_address) >> ((vram_address >> 4 & 0x04) | (vram_address & 0x02))); break;

                        case 4:
                        {
                            temp            = HClock >> 3 & 7;
                            temp_comparator = (VClock - oam_secondary[temp * 4]) ^ ((oam_secondary[(temp * 4) + 2] & 0x80) != 0 ? 0x0F : 0x00);
                            if (spr_size16 == 0x10)
                            {
                                spr_fetch_address = (oam_secondary[(temp * 4) + 1] << 0x0C & 0x1000) | (oam_secondary[(temp * 4) + 1] << 0x04 & 0x0FE0) |
                                                    (temp_comparator << 0x01 & 0x0010) | (temp_comparator & 0x0007);
                            }
                            else
                            {
                                spr_fetch_address = spr_patternAddress | (oam_secondary[(temp * 4) + 1] << 0x04) | (temp_comparator & 0x0007);
                            }
                            board.OnPPUAddressUpdate(ref spr_fetch_address);
                            break;
                        }

                        case 5:
                        {
                            spr_fetch_bit0 = board.ReadCHR(ref spr_fetch_address, true);
                            if ((oam_secondary[((HClock >> 3 & 7) * 4) + 2] & 0x40) != 0)
                            {
                                spr_fetch_bit0 = reverseLookup[spr_fetch_bit0];
                            }
                            break;
                        }

                        case 6:
                        {
                            spr_fetch_address = spr_fetch_address | 0x08;
                            board.OnPPUAddressUpdate(ref spr_fetch_address);
                            break;
                        }

                        case 7:
                        {
                            spr_fetch_bit1 = board.ReadCHR(ref spr_fetch_address, true);
                            if ((oam_secondary[((HClock >> 3 & 7) * 4) + 2] & 0x40) != 0)
                            {
                                spr_fetch_bit1 = reverseLookup[spr_fetch_bit1];
                            }

                            spr_fetch_attr = oam_secondary[((HClock >> 3 & 7) * 4) + 2];

                            // Render SPR tile
                            temp = HClock >> 3 & 7;

                            if (oam_secondary[(temp * 4) + 3] == 255)
                            {
                                break;
                            }

                            spr_pos = oam_secondary[(temp * 4) + 3];
                            object0 = spr_zero_buffer[temp] ? 0x4000 : 0x0000;
                            infront = ((oam_secondary[(temp * 4) + 2] & 0x20) == 0) ? 0x8000 : 0x0000;
                            for (spr_render_i = 0; spr_render_i < 8 && spr_pos < 256; spr_render_i++, spr_pos++, spr_fetch_bit0 <<= 1, spr_fetch_bit1 <<= 1)
                            {
                                if (spr_pos > 255)
                                {
                                    break;
                                }

                                spr_render_temp_pixel = (spr_fetch_attr << 2 & 12) | (spr_fetch_bit0 >> 7 & 1) | (spr_fetch_bit1 >> 6 & 2) | object0 | infront;
                                if ((spr_pixels[spr_pos] & 0x03) == 0 && (spr_render_temp_pixel & 0x03) != 0)
                                {
                                    spr_pixels[spr_pos] = spr_render_temp_pixel;
                                }
                            }
                            break;
                        }
                        }
                        #endregion
                        if (HClock == 256)// 257 in the Ntsc_timing diagram
                        {
                            vram_address = (vram_address & 0x7BE0) | (vram_temp & 0x041F);
                        }
                        // 280-304 in the Ntsc_timing diagram
                        if (VClock == vbl_vclock_End && HClock >= 279 && HClock <= 303)
                        {
                            vram_address = (vram_address & 0x041F) | (vram_temp & 0x7BE0);
                        }
                    }
                    else if (HClock < 336)
                    {
                        #region FIRST 2 BKG TILES FOR 1ST SCANLINE 320 - 335
                        switch (HClock & 7)
                        {
                        case 0:
                        {
                            // Fetch address of nametable
                            bkg_fetch_address = 0x2000 | (vram_address & 0x0FFF);
                            board.OnPPUAddressUpdate(ref bkg_fetch_address);
                            break;
                        }

                        case 1: bkg_fetch_nametable = board.ReadNMT(ref bkg_fetch_address); break;

                        case 2:
                        {
                            // Fetch address for attr byte
                            bkg_fetch_address = 0x23C0 | (vram_address & 0xC00) | (vram_address >> 4 & 0x38) | (vram_address >> 2 & 0x7);
                            board.OnPPUAddressUpdate(ref bkg_fetch_address);
                            break;
                        }

                        case 3: bkg_fetch_attr = (byte)(board.ReadNMT(ref bkg_fetch_address) >> ((vram_address >> 4 & 0x04) | (vram_address & 0x02))); break;

                        case 4:
                        {
                            // Fetch bit 0 address
                            bkg_fetch_address = bkg_patternAddress | (bkg_fetch_nametable << 4) | 0 | (vram_address >> 12 & 7);
                            board.OnPPUAddressUpdate(ref bkg_fetch_address);
                            break;
                        }

                        case 5: bkg_fetch_bit0 = board.ReadCHR(ref bkg_fetch_address, false); break;

                        case 6:
                        {
                            // Fetch bit 1 address
                            bkg_fetch_address = bkg_patternAddress | (bkg_fetch_nametable << 4) | 8 | (vram_address >> 12 & 7);
                            board.OnPPUAddressUpdate(ref bkg_fetch_address);
                            break;
                        }

                        case 7:
                        {
                            bkg_fetch_bit1 = board.ReadCHR(ref bkg_fetch_address, false);

                            // Increment X
                            if ((vram_address & 0x001F) == 0x001F)
                            {
                                vram_address ^= 0x041F;
                            }
                            else
                            {
                                vram_address++;
                            }


                            // Render BKG tile
                            bkg_pos = (HClock + 9) % 336;

                            for (bkg_render_i = 0; bkg_render_i < 8 && bkg_pos < 272; bkg_render_i++, bkg_pos++, bkg_fetch_bit0 <<= 1, bkg_fetch_bit1 <<= 1)
                            {
                                bkg_pixels[bkg_pos] = (bkg_fetch_attr << 2 & 12) | (bkg_fetch_bit0 >> 7 & 1) | (bkg_fetch_bit1 >> 6 & 2);
                            }

                            break;
                        }
                        }
                        #endregion
                    }
                    else if (HClock < 340)
                    {
                        #region DUMMY FETCHES 336 - 339
                        switch (HClock & 7)
                        {
                        case 0:
                        {
                            // Fetch address of nametable
                            bkg_fetch_address = 0x2000 | (vram_address & 0x0FFF);
                            board.OnPPUAddressUpdate(ref bkg_fetch_address);
                            break;
                        }

                        case 1:
                        {
                            bkg_fetch_nametable = board.ReadNMT(ref bkg_fetch_address);
                            break;
                        }
                        }
                        #endregion
                    }
                    else
                    {
                        // Idle cycle
                    }
                }
                else
                {
                    #region Rendering is off, draw color at vram address if it in range 0x3F00 - 0x3FFF
                    if (HClock < 255 & VClock < 240)
                    {
                        if ((vram_address & 0x3F00) == 0x3F00)
                        {
                            if (!screenPointerMode)
                            {
                                screen[(VClock * 256) + HClock] = palette[paletteIndexes[palettes_bank[vram_address & ((vram_address & 0x03) == 0 ? 0x0C : 0x1F)]
                                                                                         & (grayscale | emphasis)]];
                            }
                            else
                            {
                                screenPointerPos = (VClock * 256) + HClock;
                                if (screenPointerPos >= screenPointerStart && screenPointerPos < screenPointerSize)
                                {
                                    screenPointer[screenPointerPos - screenPointerStart] = palette[paletteIndexes[palettes_bank[vram_address & ((vram_address & 0x03) == 0 ? 0x0C : 0x1F)]
                                                                                                                  & (grayscale | emphasis)]];
                                }
                            }
                        }
                        else
                        {
                            if (!screenPointerMode)
                            {
                                screen[(VClock * 256) + HClock] = palette[paletteIndexes[palettes_bank[0] & (grayscale | emphasis)]];
                            }
                            else
                            {
                                screenPointerPos = (VClock * 256) + HClock;
                                if (screenPointerPos >= screenPointerStart && screenPointerPos < screenPointerSize)
                                {
                                    screenPointer[screenPointerPos - screenPointerStart] = palette[paletteIndexes[palettes_bank[0] & (grayscale | emphasis)]];
                                }
                            }
                        }
                    }
                    #endregion
                }
            }

            // Clock Horz
            HClock++;
            // Update vbl flag from latch
            vbl_flag = vbl_flag_temp;

            // Check for nmi
            if ((VClock == vbl_vclock_Start) && (HClock <= 3))
            {
                NMI_Current = (vbl_flag_temp & nmi_enabled);
            }

            #region odd frame in the idle cycle
            if (UseOddFrame)
            {
                if (HClock == 338 && VClock == vbl_vclock_End)
                {
                    oddSwap = !oddSwap;
                    if (!oddSwap & bkg_enabled)
                    {
                        HClock++;
                    }
                }
            }
            #endregion

            #region VBLANK, NMI and frame end
            if (HClock == 341)
            {
                board.OnPPUScanlineTick();
                HClock = 0;
                VClock++;
                //set vbl
                if (VClock == vbl_vclock_Start)
                {
                    vbl_flag_temp = true;
                }
                //clear vbl
                else if (VClock == vbl_vclock_End)
                {
                    spr_0Hit      = false;
                    vbl_flag_temp = false;
                    spr_overflow  = false;
                }
                else if (VClock == frameEnd)
                {
                    VClock = 0;
                    #region Render
                    if (FrameSkipEnabled)
                    {
                        if (FrameSkipTimer == 0)
                        {
                            if (!screenPointerMode)
                            {
                                videoOut.SubmitBuffer(ref screen);
                            }
                            else
                            {
                                videoOut.OnFrameFinished();
                            }
                        }
                        if (FrameSkipTimer > 0)
                        {
                            FrameSkipTimer--;
                        }
                        else
                        {
                            FrameSkipTimer = FrameSkipReload;
                        }
                    }
                    else
                    {
                        if (!screenPointerMode)
                        {
                            videoOut.SubmitBuffer(ref screen);
                        }
                        else
                        {
                            videoOut.OnFrameFinished();
                        }
                    }

                    OnFinishFrame();
                    #endregion
                }
            }
            #endregion
        }