2011-02-27 09:45:50 +00:00
|
|
|
|
//http://nesdev.parodius.com/bbs/viewtopic.php?p=4571&sid=db4c7e35316cc5d734606dd02f11dccb
|
|
|
|
|
|
2011-06-06 10:27:42 +00:00
|
|
|
|
//todo - read http://wiki.nesdev.com/w/index.php/PPU_sprite_priority
|
|
|
|
|
|
2011-06-09 19:45:07 +00:00
|
|
|
|
//TODO - correctly emulate PPU OFF state
|
|
|
|
|
|
2013-11-04 00:36:15 +00:00
|
|
|
|
using BizHawk.Common;
|
2016-06-13 01:16:31 +00:00
|
|
|
|
using System;
|
2013-11-04 00:36:15 +00:00
|
|
|
|
|
2013-11-14 13:15:41 +00:00
|
|
|
|
namespace BizHawk.Emulation.Cores.Nintendo.NES
|
2011-02-27 09:45:50 +00:00
|
|
|
|
{
|
2015-01-17 21:09:33 +00:00
|
|
|
|
sealed partial class PPU
|
2011-02-27 09:45:50 +00:00
|
|
|
|
{
|
2015-01-17 21:09:33 +00:00
|
|
|
|
const int kFetchTime = 2;
|
2011-02-27 09:45:50 +00:00
|
|
|
|
|
2016-09-16 11:46:56 +00:00
|
|
|
|
struct BGDataRecord
|
|
|
|
|
{
|
2015-01-17 21:09:33 +00:00
|
|
|
|
public byte nt, at;
|
|
|
|
|
public byte pt_0, pt_1;
|
|
|
|
|
};
|
2011-02-27 09:45:50 +00:00
|
|
|
|
|
2016-09-16 11:46:56 +00:00
|
|
|
|
public short[] xbuf = new short[256 * 240];
|
|
|
|
|
|
|
|
|
|
// values here are used in sprite evaluation
|
|
|
|
|
public bool sprite_eval_write;
|
|
|
|
|
public byte read_value;
|
|
|
|
|
public int soam_index;
|
|
|
|
|
public int soam_index_prev;
|
|
|
|
|
public int soam_m_index;
|
|
|
|
|
public int oam_index;
|
|
|
|
|
public int read_value_aux;
|
|
|
|
|
public int soam_m_index_aux;
|
|
|
|
|
public int oam_index_aux;
|
|
|
|
|
public bool is_even_cycle;
|
|
|
|
|
public bool sprite_zero_in_range = false;
|
|
|
|
|
public bool sprite_zero_go = false;
|
|
|
|
|
public int yp;
|
2016-09-16 22:28:54 +00:00
|
|
|
|
public int target;
|
2016-09-16 11:46:56 +00:00
|
|
|
|
public int spriteHeight;
|
|
|
|
|
public byte[] soam = new byte[512]; // in a real nes, this would only be 32, but we wish to allow more then 8 sprites per scanline
|
|
|
|
|
public bool reg_2001_color_disable_latch; // the value used here is taken
|
2017-02-04 14:14:55 +00:00
|
|
|
|
public bool conflict_2006;
|
2016-09-16 11:46:56 +00:00
|
|
|
|
|
|
|
|
|
struct TempOAM
|
|
|
|
|
{
|
|
|
|
|
public byte oam_y;
|
|
|
|
|
public byte oam_ind;
|
|
|
|
|
public byte oam_attr;
|
|
|
|
|
public byte oam_x;
|
|
|
|
|
public byte patterns_0;
|
|
|
|
|
public byte patterns_1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TempOAM[] t_oam = new TempOAM[64];
|
|
|
|
|
|
|
|
|
|
int ppu_addr_temp;
|
2015-01-17 21:09:33 +00:00
|
|
|
|
void Read_bgdata(ref BGDataRecord bgdata)
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < 8; i++)
|
2016-09-16 11:46:56 +00:00
|
|
|
|
Read_bgdata(i, ref bgdata);
|
2015-01-17 21:09:33 +00:00
|
|
|
|
}
|
2016-09-16 11:46:56 +00:00
|
|
|
|
|
|
|
|
|
// attempt to emulate graphics pipeline behaviour
|
|
|
|
|
// experimental
|
|
|
|
|
int pixelcolor_latch_1;
|
|
|
|
|
int pixelcolor_latch_2;
|
2016-09-16 22:28:54 +00:00
|
|
|
|
void pipeline(int pixelcolor, int target, int row_check)
|
2016-09-16 11:46:56 +00:00
|
|
|
|
{
|
2016-09-16 22:28:54 +00:00
|
|
|
|
if (row_check > 1)
|
2016-09-16 11:46:56 +00:00
|
|
|
|
{
|
|
|
|
|
if (reg_2001.color_disable)
|
|
|
|
|
pixelcolor_latch_2 &= 0x30;
|
|
|
|
|
|
|
|
|
|
xbuf[(target - 2)] = PaletteAdjustPixel(pixelcolor_latch_2);
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-17 00:37:11 +00:00
|
|
|
|
if (row_check >= 1)
|
2016-09-16 11:46:56 +00:00
|
|
|
|
{
|
|
|
|
|
pixelcolor_latch_2 = pixelcolor_latch_1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pixelcolor_latch_1 = pixelcolor;
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-17 21:09:33 +00:00
|
|
|
|
void Read_bgdata(int cycle, ref BGDataRecord bgdata)
|
|
|
|
|
{
|
|
|
|
|
switch (cycle)
|
2012-03-25 09:25:27 +00:00
|
|
|
|
{
|
2015-01-17 21:09:33 +00:00
|
|
|
|
case 0:
|
|
|
|
|
ppu_addr_temp = ppur.get_ntread();
|
2016-10-24 00:50:06 +00:00
|
|
|
|
bgdata.nt = ppubus_read(ppu_addr_temp, true, true);
|
2015-01-17 21:09:33 +00:00
|
|
|
|
runppu(1);
|
|
|
|
|
break;
|
|
|
|
|
case 1:
|
|
|
|
|
runppu(1);
|
|
|
|
|
break;
|
|
|
|
|
case 2:
|
|
|
|
|
{
|
|
|
|
|
ppu_addr_temp = ppur.get_atread();
|
2016-10-24 00:50:06 +00:00
|
|
|
|
byte at = ppubus_read(ppu_addr_temp, true, true);
|
2015-01-17 21:09:33 +00:00
|
|
|
|
|
|
|
|
|
//modify at to get appropriate palette shift
|
|
|
|
|
if ((ppur.vt & 2) != 0) at >>= 4;
|
|
|
|
|
if ((ppur.ht & 2) != 0) at >>= 2;
|
|
|
|
|
at &= 0x03;
|
|
|
|
|
at <<= 2;
|
|
|
|
|
bgdata.at = at;
|
2012-03-25 09:25:27 +00:00
|
|
|
|
runppu(1);
|
2017-01-26 00:17:24 +00:00
|
|
|
|
|
2012-03-25 09:25:27 +00:00
|
|
|
|
break;
|
2015-01-17 21:09:33 +00:00
|
|
|
|
}
|
|
|
|
|
case 3:
|
|
|
|
|
runppu(1);
|
|
|
|
|
break;
|
|
|
|
|
case 4:
|
|
|
|
|
ppu_addr_temp = ppur.get_ptread(bgdata.nt);
|
2016-10-24 00:50:06 +00:00
|
|
|
|
bgdata.pt_0 = ppubus_read(ppu_addr_temp, true, true);
|
2015-01-17 21:09:33 +00:00
|
|
|
|
runppu(1);
|
|
|
|
|
break;
|
|
|
|
|
case 5:
|
|
|
|
|
runppu(1);
|
|
|
|
|
break;
|
|
|
|
|
case 6:
|
|
|
|
|
ppu_addr_temp |= 8;
|
2016-10-24 00:50:06 +00:00
|
|
|
|
bgdata.pt_1 = ppubus_read(ppu_addr_temp, true, true);
|
2015-01-17 21:09:33 +00:00
|
|
|
|
runppu(1);
|
2017-01-26 00:17:24 +00:00
|
|
|
|
|
2017-02-04 14:14:55 +00:00
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
case 7:
|
|
|
|
|
|
|
|
|
|
runppu(1);
|
2017-01-26 00:17:24 +00:00
|
|
|
|
//horizontal scroll clocked at cycle 3 and then
|
2017-02-04 14:14:55 +00:00
|
|
|
|
//vertical scroll at 256
|
2017-01-26 00:17:24 +00:00
|
|
|
|
if (reg_2001.PPUON)
|
|
|
|
|
{
|
|
|
|
|
ppur.increment_hsc();
|
2017-02-04 14:14:55 +00:00
|
|
|
|
if (ppur.status.cycle == 256 && !conflict_2006)
|
2017-01-26 00:17:24 +00:00
|
|
|
|
ppur.increment_vs();
|
|
|
|
|
}
|
2017-02-04 14:14:55 +00:00
|
|
|
|
conflict_2006 = false;
|
2015-01-17 21:09:33 +00:00
|
|
|
|
break;
|
|
|
|
|
} //switch(cycle)
|
|
|
|
|
}
|
2011-02-27 09:45:50 +00:00
|
|
|
|
|
2015-01-17 21:09:33 +00:00
|
|
|
|
//TODO - check flashing sirens in werewolf
|
|
|
|
|
short PaletteAdjustPixel(int pixel)
|
|
|
|
|
{
|
|
|
|
|
//tack on the deemph bits. THESE MAY BE ORDERED WRONG. PLEASE CHECK IN THE PALETTE CODE
|
|
|
|
|
return (short)(pixel | reg_2001.intensity_lsl_6);
|
|
|
|
|
}
|
2011-02-27 09:45:50 +00:00
|
|
|
|
|
2015-01-17 21:09:33 +00:00
|
|
|
|
const int kLineTime = 341;
|
|
|
|
|
public unsafe void FrameAdvance()
|
|
|
|
|
{
|
2016-09-16 11:46:56 +00:00
|
|
|
|
BGDataRecord* bgdata = stackalloc BGDataRecord[34]; //one at the end is junk, it can never be rendered
|
2011-02-27 09:45:50 +00:00
|
|
|
|
|
2015-10-13 01:32:06 +00:00
|
|
|
|
//262 scanlines
|
|
|
|
|
if (ppudead != 0)
|
2015-01-17 21:09:33 +00:00
|
|
|
|
{
|
|
|
|
|
FrameAdvance_ppudead();
|
|
|
|
|
return;
|
|
|
|
|
}
|
2011-02-27 09:45:50 +00:00
|
|
|
|
|
2015-01-17 21:09:33 +00:00
|
|
|
|
Reg2002_vblank_active_pending = true;
|
|
|
|
|
ppuphase = PPUPHASE.VBL;
|
|
|
|
|
ppur.status.sl = 241;
|
2011-02-27 09:45:50 +00:00
|
|
|
|
|
2016-09-16 11:46:56 +00:00
|
|
|
|
//Not sure if this is correct. According to Matt Conte and my own tests, it is. Timing is probably off, though.
|
|
|
|
|
//NOTE: Not having this here breaks a Super Donkey Kong game.
|
|
|
|
|
if (reg_2001.show_obj || reg_2001.show_bg) reg_2003 = 0;
|
2011-02-27 09:45:50 +00:00
|
|
|
|
|
2016-09-16 11:46:56 +00:00
|
|
|
|
//this was repeatedly finetuned from the fceux days thrugh the old cpu core and into the new one to pass 05-nmi_timing.nes
|
|
|
|
|
//note that there is still some leniency. for instance, 4,2 will pass in addition to 3,3
|
|
|
|
|
const int delay = 6;
|
2015-01-17 21:09:33 +00:00
|
|
|
|
runppu(3);
|
|
|
|
|
bool nmi_destiny = reg_2000.vblank_nmi_gen && Reg2002_vblank_active;
|
|
|
|
|
runppu(3);
|
|
|
|
|
if (nmi_destiny) TriggerNMI();
|
2015-03-11 09:46:27 +00:00
|
|
|
|
nes.Board.AtVsyncNMI();
|
2015-01-17 21:09:33 +00:00
|
|
|
|
runppu(postNMIlines * kLineTime - delay);
|
2011-02-27 09:45:50 +00:00
|
|
|
|
|
2015-01-17 21:09:33 +00:00
|
|
|
|
//this seems to run just before the dummy scanline begins
|
|
|
|
|
clear_2002();
|
2011-02-27 09:45:50 +00:00
|
|
|
|
|
2015-01-17 21:09:33 +00:00
|
|
|
|
idleSynch ^= true;
|
2012-03-18 18:41:45 +00:00
|
|
|
|
|
2015-01-17 21:09:33 +00:00
|
|
|
|
//render 241 scanlines (including 1 dummy at beginning)
|
|
|
|
|
for (int sl = 0; sl < 241; sl++)
|
|
|
|
|
{
|
|
|
|
|
ppur.status.cycle = 0;
|
2012-03-18 21:46:56 +00:00
|
|
|
|
|
2015-01-17 21:09:33 +00:00
|
|
|
|
ppur.status.sl = sl;
|
2011-02-27 09:45:50 +00:00
|
|
|
|
|
2016-09-16 11:46:56 +00:00
|
|
|
|
soam_index = 0;
|
|
|
|
|
soam_m_index = 0;
|
|
|
|
|
soam_m_index_aux = 0;
|
|
|
|
|
oam_index_aux = 0;
|
|
|
|
|
oam_index = 0;
|
|
|
|
|
is_even_cycle = true;
|
|
|
|
|
sprite_eval_write = true;
|
|
|
|
|
sprite_zero_go = false;
|
|
|
|
|
if (sprite_zero_in_range)
|
|
|
|
|
sprite_zero_go = true;
|
2016-06-13 01:16:31 +00:00
|
|
|
|
|
2016-09-16 11:46:56 +00:00
|
|
|
|
sprite_zero_in_range = false;
|
2016-06-13 01:16:31 +00:00
|
|
|
|
|
|
|
|
|
yp = sl - 1;
|
2015-01-17 21:09:33 +00:00
|
|
|
|
ppuphase = PPUPHASE.BG;
|
2011-02-27 09:45:50 +00:00
|
|
|
|
|
2015-01-17 21:09:33 +00:00
|
|
|
|
if (NTViewCallback != null && yp == NTViewCallback.Scanline) NTViewCallback.Callback();
|
|
|
|
|
if (PPUViewCallback != null && yp == PPUViewCallback.Scanline) PPUViewCallback.Callback();
|
2011-06-10 07:43:48 +00:00
|
|
|
|
|
2016-09-16 11:46:56 +00:00
|
|
|
|
//ok, we're also going to draw here.
|
|
|
|
|
//unless we're on the first dummy scanline
|
|
|
|
|
if (sl != 0)
|
2015-01-17 21:09:33 +00:00
|
|
|
|
{
|
|
|
|
|
//the main scanline rendering loop:
|
|
|
|
|
//32 times, we will fetch a tile and then render 8 pixels.
|
|
|
|
|
//two of those tiles were read in the last scanline.
|
|
|
|
|
int yp_shift = yp << 8;
|
|
|
|
|
for (int xt = 0; xt < 32; xt++)
|
2012-05-06 06:04:00 +00:00
|
|
|
|
{
|
2015-01-17 21:09:33 +00:00
|
|
|
|
int xstart = xt << 3;
|
2016-09-16 22:28:54 +00:00
|
|
|
|
target = yp_shift + xstart;
|
2015-01-17 21:09:33 +00:00
|
|
|
|
int rasterpos = xstart;
|
2011-02-27 09:45:50 +00:00
|
|
|
|
|
2016-09-16 11:46:56 +00:00
|
|
|
|
spriteHeight = reg_2000.obj_size_16 ? 16 : 8;
|
2016-06-13 01:16:31 +00:00
|
|
|
|
|
2016-09-16 11:46:56 +00:00
|
|
|
|
//check all the conditions that can cause things to render in these 8px
|
|
|
|
|
bool renderspritenow = reg_2001.show_obj && (xt > 0 || reg_2001.show_obj_leftmost);
|
|
|
|
|
bool renderbgnow;
|
2011-02-27 09:45:50 +00:00
|
|
|
|
|
2015-01-17 21:09:33 +00:00
|
|
|
|
for (int xp = 0; xp < 8; xp++, rasterpos++)
|
|
|
|
|
{
|
2016-09-15 20:06:31 +00:00
|
|
|
|
//////////////////////////////////////////////////
|
2016-09-16 11:46:56 +00:00
|
|
|
|
//Sprite Evaluation Start
|
2016-09-15 20:06:31 +00:00
|
|
|
|
//////////////////////////////////////////////////
|
2016-09-16 11:46:56 +00:00
|
|
|
|
if (ppur.status.cycle <= 63 && !is_even_cycle)
|
|
|
|
|
{
|
|
|
|
|
// the first 64 cycles of each scanline are used to initialize sceondary OAM
|
|
|
|
|
// the actual effect setting a flag that always returns 0xFF from a OAM read
|
|
|
|
|
// this is a bit of a shortcut to save some instructions
|
|
|
|
|
// data is read from OAM as normal but never used
|
|
|
|
|
soam[soam_index] = 0xFF;
|
|
|
|
|
soam_index++;
|
|
|
|
|
}
|
|
|
|
|
if (ppur.status.cycle == 64)
|
|
|
|
|
soam_index = 0;
|
|
|
|
|
|
|
|
|
|
// otherwise, scan through OAM and test if sprites are in range
|
|
|
|
|
// if they are, they get copied to the secondary OAM
|
|
|
|
|
if (ppur.status.cycle >= 64)
|
|
|
|
|
{
|
|
|
|
|
if (oam_index == 64)
|
|
|
|
|
{
|
|
|
|
|
sprite_eval_write = false;
|
|
|
|
|
}
|
2016-09-15 20:06:31 +00:00
|
|
|
|
|
2017-01-27 23:34:56 +00:00
|
|
|
|
if (is_even_cycle && oam_index<64)
|
2016-09-16 11:46:56 +00:00
|
|
|
|
{
|
|
|
|
|
read_value = OAM[oam_index * 4 + soam_m_index];
|
|
|
|
|
|
|
|
|
|
if (oam_index_aux > 63)
|
|
|
|
|
oam_index_aux = 63;
|
|
|
|
|
|
|
|
|
|
read_value_aux = OAM[oam_index_aux * 4 + soam_m_index_aux];
|
|
|
|
|
}
|
|
|
|
|
else if (sprite_eval_write)
|
|
|
|
|
{
|
|
|
|
|
if (soam_index >= 8)
|
|
|
|
|
{
|
|
|
|
|
// this code mirrors sprite overflow bug behaviour
|
|
|
|
|
// see http://wiki.nesdev.com/w/index.php/PPU_sprite_evaluation
|
|
|
|
|
if (yp >= read_value && yp < read_value + spriteHeight && reg_2001.PPUON)
|
|
|
|
|
{
|
|
|
|
|
Reg2002_objoverflow = true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
soam_m_index++;
|
|
|
|
|
oam_index++;
|
|
|
|
|
if (soam_m_index == 4)
|
|
|
|
|
soam_m_index = 0;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//look for sprites
|
|
|
|
|
soam[soam_index * 4] = OAM[oam_index_aux * 4];
|
|
|
|
|
if (yp >= read_value_aux && yp < read_value_aux + spriteHeight && soam_m_index_aux == 0)
|
|
|
|
|
{
|
|
|
|
|
//a flag gets set if sprite zero is in range
|
|
|
|
|
if (oam_index_aux == 0)
|
|
|
|
|
sprite_zero_in_range = true;
|
|
|
|
|
|
|
|
|
|
soam_m_index_aux++;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
else if (soam_m_index_aux > 0 && soam_m_index_aux < 4)
|
|
|
|
|
{
|
|
|
|
|
soam[soam_index * 4 + soam_m_index_aux] = OAM[oam_index_aux * 4 + soam_m_index_aux];
|
|
|
|
|
soam_m_index_aux++;
|
|
|
|
|
if (soam_m_index_aux == 4)
|
|
|
|
|
{
|
|
|
|
|
oam_index_aux++;
|
|
|
|
|
soam_index++;
|
|
|
|
|
soam_m_index_aux = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
oam_index_aux++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (soam_index < 8)
|
|
|
|
|
{
|
|
|
|
|
soam_m_index = soam_m_index_aux;
|
|
|
|
|
oam_index = oam_index_aux;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
2017-01-27 23:34:56 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-09-16 11:46:56 +00:00
|
|
|
|
//////////////////////////////////////////////////
|
|
|
|
|
//Sprite Evaluation End
|
|
|
|
|
//////////////////////////////////////////////////
|
2017-01-27 23:34:56 +00:00
|
|
|
|
|
2016-09-15 20:06:31 +00:00
|
|
|
|
//process the current clock's worth of bg data fetching
|
|
|
|
|
//this needs to be split into 8 pieces or else exact sprite 0 hitting wont work due to the cpu not running while the sprite renders below
|
2016-09-25 15:52:12 +00:00
|
|
|
|
if (reg_2001.show_obj || reg_2001.show_bg)
|
|
|
|
|
Read_bgdata(xp, ref bgdata[xt + 2]);
|
|
|
|
|
else
|
|
|
|
|
runppu(1);
|
2012-03-25 09:25:27 +00:00
|
|
|
|
|
2016-09-16 22:28:54 +00:00
|
|
|
|
renderbgnow = reg_2001.show_bg && (xt > 0 || reg_2001.show_bg_leftmost);
|
2015-01-17 21:09:33 +00:00
|
|
|
|
//bg pos is different from raster pos due to its offsetability.
|
|
|
|
|
//so adjust for that here
|
|
|
|
|
int bgpos = rasterpos + ppur.fh;
|
|
|
|
|
int bgpx = bgpos & 7;
|
|
|
|
|
int bgtile = bgpos >> 3;
|
2011-02-27 09:45:50 +00:00
|
|
|
|
|
2015-09-06 17:02:22 +00:00
|
|
|
|
int pixel = 0, pixelcolor = PALRAM[pixel];
|
|
|
|
|
|
|
|
|
|
//according to qeed's doc, use palette 0 or $2006's value if it is & 0x3Fxx
|
|
|
|
|
//at one point I commented this out to fix bottom-left garbage in DW4. but it's needed for full_nes_palette.
|
|
|
|
|
//solution is to only run when PPU is actually OFF (left-suppression doesnt count)
|
|
|
|
|
if (!reg_2001.show_bg && !reg_2001.show_obj)
|
|
|
|
|
{
|
|
|
|
|
// if there's anything wrong with how we're doing this, someone please chime in
|
|
|
|
|
int addr = ppur.get_2007access();
|
|
|
|
|
if ((addr & 0x3F00) == 0x3F00)
|
|
|
|
|
{
|
|
|
|
|
pixel = addr & 0x1F;
|
|
|
|
|
}
|
|
|
|
|
pixelcolor = PALRAM[pixel];
|
|
|
|
|
pixelcolor |= 0x8000; //whats this? i think its a flag to indicate a hidden background to be used by the canvas filling logic later
|
|
|
|
|
}
|
2011-02-27 09:45:50 +00:00
|
|
|
|
|
2015-01-17 21:09:33 +00:00
|
|
|
|
//generate the BG data
|
|
|
|
|
if (renderbgnow)
|
|
|
|
|
{
|
|
|
|
|
byte pt_0 = bgdata[bgtile].pt_0;
|
|
|
|
|
byte pt_1 = bgdata[bgtile].pt_1;
|
|
|
|
|
int sel = 7 - bgpx;
|
|
|
|
|
pixel = ((pt_0 >> sel) & 1) | (((pt_1 >> sel) & 1) << 1);
|
|
|
|
|
if (pixel != 0)
|
|
|
|
|
pixel |= bgdata[bgtile].at;
|
|
|
|
|
pixelcolor = PALRAM[pixel];
|
|
|
|
|
}
|
2011-02-27 09:45:50 +00:00
|
|
|
|
|
2015-01-17 21:09:33 +00:00
|
|
|
|
if (!nes.Settings.DispBackground)
|
|
|
|
|
pixelcolor = 0x8000; //whats this? i think its a flag to indicate a hidden background to be used by the canvas filling logic later
|
2016-09-16 11:46:56 +00:00
|
|
|
|
|
|
|
|
|
//look for a sprite to be drawn
|
|
|
|
|
bool havepixel = false;
|
2016-06-13 01:16:31 +00:00
|
|
|
|
for (int s = 0; s < soam_index_prev; s++)
|
2015-01-17 21:09:33 +00:00
|
|
|
|
{
|
2016-06-13 01:16:31 +00:00
|
|
|
|
int x = t_oam[s].oam_x;
|
2015-01-17 21:09:33 +00:00
|
|
|
|
if (rasterpos >= x && rasterpos < x + 8)
|
2011-02-27 09:45:50 +00:00
|
|
|
|
{
|
2015-01-17 21:09:33 +00:00
|
|
|
|
//build the pixel.
|
|
|
|
|
//fetch the LSB of the patterns
|
2016-06-13 01:16:31 +00:00
|
|
|
|
int spixel = t_oam[s].patterns_0 & 1;
|
|
|
|
|
spixel |= (t_oam[s].patterns_1 & 1) << 1;
|
2015-01-17 21:09:33 +00:00
|
|
|
|
|
2016-09-16 11:46:56 +00:00
|
|
|
|
//shift down the patterns so the next pixel is in the LSB
|
|
|
|
|
t_oam[s].patterns_0 >>= 1;
|
|
|
|
|
t_oam[s].patterns_1 >>= 1;
|
2015-01-17 21:09:33 +00:00
|
|
|
|
|
2016-09-16 11:46:56 +00:00
|
|
|
|
//bail out if we already have a pixel from a higher priority sprite.
|
|
|
|
|
//notice that we continue looping anyway, so that we can shift down the patterns
|
|
|
|
|
//transparent pixel bailout
|
|
|
|
|
if (!renderspritenow || havepixel || spixel == 0) continue;
|
2015-01-17 21:09:33 +00:00
|
|
|
|
|
|
|
|
|
havepixel = true;
|
2016-09-16 11:46:56 +00:00
|
|
|
|
|
2015-01-17 21:09:33 +00:00
|
|
|
|
//TODO - make sure we dont trigger spritehit if the edges are masked for either BG or OBJ
|
|
|
|
|
//spritehit:
|
|
|
|
|
//1. is it sprite#0?
|
|
|
|
|
//2. is the bg pixel nonzero?
|
|
|
|
|
//then, it is spritehit.
|
2016-09-16 11:46:56 +00:00
|
|
|
|
Reg2002_objhit |= (sprite_zero_go && s == 0 && pixel != 0 && rasterpos < 255 && reg_2001.show_bg && reg_2001.show_obj);
|
2016-06-13 01:16:31 +00:00
|
|
|
|
|
2015-01-17 21:09:33 +00:00
|
|
|
|
//priority handling, if in front of BG:
|
2016-06-13 01:16:31 +00:00
|
|
|
|
bool drawsprite = !(((t_oam[s].oam_attr & 0x20) != 0) && ((pixel & 3) != 0));
|
2015-01-17 21:09:33 +00:00
|
|
|
|
if (drawsprite && nes.Settings.DispSprites)
|
2011-02-27 09:45:50 +00:00
|
|
|
|
{
|
2015-01-17 21:09:33 +00:00
|
|
|
|
//bring in the palette bits and palettize
|
2016-06-13 01:16:31 +00:00
|
|
|
|
spixel |= (t_oam[s].oam_attr & 3) << 2;
|
2015-01-17 21:09:33 +00:00
|
|
|
|
//save it for use in the framebuffer
|
2016-09-16 11:46:56 +00:00
|
|
|
|
pixelcolor = PALRAM[0x10 + spixel];
|
2015-01-17 21:09:33 +00:00
|
|
|
|
}
|
|
|
|
|
} //rasterpos in sprite range
|
|
|
|
|
} //oamcount loop
|
2016-09-16 11:46:56 +00:00
|
|
|
|
/*
|
|
|
|
|
if (reg_2001.color_disable)
|
|
|
|
|
pixelcolor &= 0x30;
|
|
|
|
|
xbuf[target] = PaletteAdjustPixel(pixelcolor);
|
|
|
|
|
*/
|
2016-09-16 22:28:54 +00:00
|
|
|
|
pipeline(pixelcolor, target, xt*32+xp);
|
2015-01-17 21:09:33 +00:00
|
|
|
|
target++;
|
2016-09-16 22:28:54 +00:00
|
|
|
|
|
2015-01-17 21:09:33 +00:00
|
|
|
|
} //loop across 8 pixels
|
|
|
|
|
} //loop across 32 tiles
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
for (int xt = 0; xt < 32; xt++)
|
2016-09-16 11:46:56 +00:00
|
|
|
|
Read_bgdata(ref bgdata[xt + 2]);
|
2011-02-27 09:45:50 +00:00
|
|
|
|
|
2016-09-16 11:46:56 +00:00
|
|
|
|
// normally only 8 sprites are allowed, but with a particular setting we can have more then that
|
2017-01-27 23:34:56 +00:00
|
|
|
|
// this extra bit is here because the actual loop doesn't have enough time to scann all sprites if there are more then 8
|
|
|
|
|
if (nes.Settings.AllowMoreThanEightSprites)
|
|
|
|
|
{
|
|
|
|
|
while (oam_index_aux < 64)
|
|
|
|
|
{
|
|
|
|
|
//look for sprites
|
|
|
|
|
soam[soam_index * 4] = OAM[oam_index_aux * 4];
|
|
|
|
|
read_value_aux = OAM[oam_index_aux * 4];
|
|
|
|
|
if (yp >= read_value_aux && yp < read_value_aux + spriteHeight)
|
|
|
|
|
{
|
|
|
|
|
soam[soam_index * 4 + 1] = OAM[oam_index_aux * 4 + 1];
|
|
|
|
|
soam[soam_index * 4 + 2] = OAM[oam_index_aux * 4 + 2];
|
|
|
|
|
soam[soam_index * 4 + 3] = OAM[oam_index_aux * 4 + 3];
|
|
|
|
|
soam_index++;
|
|
|
|
|
oam_index_aux++;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
oam_index_aux++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-16 11:46:56 +00:00
|
|
|
|
soam_index_prev = soam_index;
|
2017-01-27 23:34:56 +00:00
|
|
|
|
|
2016-09-16 11:46:56 +00:00
|
|
|
|
if (soam_index_prev > 8 && !nes.Settings.AllowMoreThanEightSprites)
|
|
|
|
|
soam_index_prev = 8;
|
2011-02-28 06:16:20 +00:00
|
|
|
|
|
2016-06-13 01:16:31 +00:00
|
|
|
|
ppuphase = PPUPHASE.OBJ;
|
2011-02-28 06:16:20 +00:00
|
|
|
|
|
2016-09-16 11:46:56 +00:00
|
|
|
|
spriteHeight = reg_2000.obj_size_16 ? 16 : 8;
|
2011-02-27 09:45:50 +00:00
|
|
|
|
|
2016-09-16 11:46:56 +00:00
|
|
|
|
// if there are less then 8 evaluated sprites, we still process 8 sprites
|
|
|
|
|
int bound;
|
2011-02-27 09:45:50 +00:00
|
|
|
|
|
2016-09-16 11:46:56 +00:00
|
|
|
|
if (soam_index_prev > 8)
|
|
|
|
|
{
|
|
|
|
|
bound = soam_index_prev;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
bound = 8;
|
|
|
|
|
}
|
2016-06-13 01:16:31 +00:00
|
|
|
|
|
2017-01-27 23:34:56 +00:00
|
|
|
|
for (int s = 0; s < 8; s++)
|
2015-01-17 21:09:33 +00:00
|
|
|
|
{
|
|
|
|
|
//if this is a real sprite sprite, then it is not above the 8 sprite limit.
|
|
|
|
|
//this is how we support the no 8 sprite limit feature.
|
|
|
|
|
//not that at some point we may need a virtual CALL_PPUREAD which just peeks and doesnt increment any counters
|
|
|
|
|
//this could be handy for the debugging tools also
|
|
|
|
|
bool realSprite = (s < 8);
|
2016-06-13 01:16:31 +00:00
|
|
|
|
bool junksprite = (!reg_2001.PPUON);
|
|
|
|
|
|
2016-09-16 11:46:56 +00:00
|
|
|
|
t_oam[s].oam_y = soam[s * 4];
|
|
|
|
|
t_oam[s].oam_ind = soam[s * 4 + 1];
|
|
|
|
|
t_oam[s].oam_attr = soam[s * 4 + 2];
|
|
|
|
|
t_oam[s].oam_x = soam[s * 4 + 3];
|
2016-06-13 01:16:31 +00:00
|
|
|
|
|
2016-09-16 11:46:56 +00:00
|
|
|
|
int line = yp - t_oam[s].oam_y;
|
2016-06-13 01:16:31 +00:00
|
|
|
|
if ((t_oam[s].oam_attr & 0x80) != 0) //vflip
|
2015-01-17 21:09:33 +00:00
|
|
|
|
line = spriteHeight - line - 1;
|
|
|
|
|
|
2016-06-13 01:16:31 +00:00
|
|
|
|
int patternNumber = t_oam[s].oam_ind;
|
2015-01-17 21:09:33 +00:00
|
|
|
|
int patternAddress;
|
|
|
|
|
|
|
|
|
|
//8x16 sprite handling:
|
|
|
|
|
if (reg_2000.obj_size_16)
|
|
|
|
|
{
|
|
|
|
|
int bank = (patternNumber & 1) << 12;
|
|
|
|
|
patternNumber = patternNumber & ~1;
|
|
|
|
|
patternNumber |= (line >> 3) & 1;
|
|
|
|
|
patternAddress = (patternNumber << 4) | bank;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
patternAddress = (patternNumber << 4) | (reg_2000.obj_pattern_hi << 12);
|
2012-05-06 06:04:00 +00:00
|
|
|
|
|
2015-01-17 21:09:33 +00:00
|
|
|
|
//offset into the pattern for the current line.
|
|
|
|
|
//tricky: tall sprites have already had lines>8 taken care of by getting a new pattern number above.
|
|
|
|
|
//so we just need the line offset for the second pattern
|
|
|
|
|
patternAddress += line & 7;
|
2012-05-06 06:04:00 +00:00
|
|
|
|
|
2015-01-17 21:09:33 +00:00
|
|
|
|
//garbage nametable fetches + scroll resets
|
|
|
|
|
int garbage_todo = 2;
|
2017-01-27 23:34:56 +00:00
|
|
|
|
|
2016-10-24 00:50:06 +00:00
|
|
|
|
ppubus_read(ppur.get_ntread(), true, true);
|
2017-01-27 23:34:56 +00:00
|
|
|
|
|
2015-01-17 21:09:33 +00:00
|
|
|
|
if (reg_2001.PPUON)
|
|
|
|
|
{
|
|
|
|
|
if (sl == 0 && ppur.status.cycle == 304)
|
2012-05-06 06:04:00 +00:00
|
|
|
|
{
|
2015-01-17 21:09:33 +00:00
|
|
|
|
runppu(1);
|
2016-09-16 11:46:56 +00:00
|
|
|
|
read_value = t_oam[s].oam_y;
|
|
|
|
|
if (reg_2001.PPUON) ppur.install_latches();
|
2015-01-17 21:09:33 +00:00
|
|
|
|
runppu(1);
|
2016-09-16 11:46:56 +00:00
|
|
|
|
read_value = t_oam[s].oam_ind;
|
|
|
|
|
garbage_todo = 0;
|
2012-05-06 06:04:00 +00:00
|
|
|
|
}
|
2015-01-17 21:09:33 +00:00
|
|
|
|
if ((sl != 0) && ppur.status.cycle == 256)
|
|
|
|
|
{
|
|
|
|
|
runppu(1);
|
2016-09-17 00:37:11 +00:00
|
|
|
|
if (target<=61441 && target > 0 && s==0)
|
2016-09-16 22:28:54 +00:00
|
|
|
|
{
|
2016-09-17 00:37:11 +00:00
|
|
|
|
pipeline(0, target,256);
|
2016-09-16 22:28:54 +00:00
|
|
|
|
target++;
|
|
|
|
|
}
|
2016-09-16 11:46:56 +00:00
|
|
|
|
read_value = t_oam[s].oam_y;
|
|
|
|
|
//at 257: 3d world runner is ugly if we do this at 256
|
|
|
|
|
if (reg_2001.PPUON) ppur.install_h_latches();
|
|
|
|
|
runppu(1);
|
2016-09-17 00:37:11 +00:00
|
|
|
|
if (target <= 61441 && target > 0 && s==0)
|
|
|
|
|
{
|
|
|
|
|
pipeline(0, target, 257); // last pipeline call option 1 of 2
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-16 11:46:56 +00:00
|
|
|
|
read_value = t_oam[s].oam_ind;
|
|
|
|
|
garbage_todo = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-01-27 23:34:56 +00:00
|
|
|
|
|
2016-09-16 11:46:56 +00:00
|
|
|
|
if (realSprite)
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < garbage_todo; i++)
|
|
|
|
|
{
|
2015-01-17 21:09:33 +00:00
|
|
|
|
runppu(1);
|
2016-09-16 11:46:56 +00:00
|
|
|
|
|
|
|
|
|
if (i == 0)
|
|
|
|
|
{
|
2016-09-17 00:37:11 +00:00
|
|
|
|
if (target <= 61441 && target > 0 && s==0)
|
2016-09-16 22:28:54 +00:00
|
|
|
|
{
|
2016-09-17 00:37:11 +00:00
|
|
|
|
pipeline(0, target,256);
|
2016-09-16 22:28:54 +00:00
|
|
|
|
target++;
|
|
|
|
|
}
|
2016-09-16 11:46:56 +00:00
|
|
|
|
read_value = t_oam[s].oam_y;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2016-09-17 00:37:11 +00:00
|
|
|
|
if (target <= 61441 && target > 0 && s==0)
|
|
|
|
|
{
|
|
|
|
|
pipeline(0, target, 257); // last pipeline call option 2 of 2
|
|
|
|
|
}
|
2016-09-16 11:46:56 +00:00
|
|
|
|
read_value = t_oam[s].oam_ind;
|
|
|
|
|
}
|
2015-01-17 21:09:33 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2011-02-27 09:45:50 +00:00
|
|
|
|
|
2012-05-06 06:04:00 +00:00
|
|
|
|
|
2016-10-24 00:50:06 +00:00
|
|
|
|
ppubus_read(ppur.get_atread(), true, true); //at or nt?
|
2016-09-16 11:46:56 +00:00
|
|
|
|
if (realSprite)
|
|
|
|
|
{
|
|
|
|
|
runppu(1);
|
|
|
|
|
read_value = t_oam[s].oam_attr;
|
|
|
|
|
runppu(1);
|
|
|
|
|
read_value = t_oam[s].oam_x;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO - fake sprites should not come through ppubus_read but rather peek it
|
|
|
|
|
// (at least, they should not probe it with AddressPPU. maybe the difference between peek and read is not necessary)
|
|
|
|
|
if (junksprite)
|
|
|
|
|
{
|
|
|
|
|
if (realSprite)
|
|
|
|
|
{
|
2016-10-24 00:50:06 +00:00
|
|
|
|
ppubus_read(patternAddress, true, false);
|
|
|
|
|
ppubus_read(patternAddress, true, false);
|
2016-09-16 11:46:56 +00:00
|
|
|
|
runppu(kFetchTime * 2);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
int addr = patternAddress;
|
2016-10-24 00:50:06 +00:00
|
|
|
|
t_oam[s].patterns_0 = ppubus_read(addr, true, true);
|
2016-09-16 11:46:56 +00:00
|
|
|
|
if (realSprite)
|
|
|
|
|
{
|
|
|
|
|
runppu(kFetchTime);
|
|
|
|
|
read_value = t_oam[s].oam_x;
|
|
|
|
|
}
|
|
|
|
|
addr += 8;
|
2016-10-24 00:50:06 +00:00
|
|
|
|
t_oam[s].patterns_1 = ppubus_read(addr, true, true);
|
2016-09-16 11:46:56 +00:00
|
|
|
|
if (realSprite)
|
|
|
|
|
{
|
|
|
|
|
runppu(kFetchTime);
|
|
|
|
|
read_value = t_oam[s].oam_x;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// hflip
|
|
|
|
|
if ((t_oam[s].oam_attr & 0x40) == 0)
|
|
|
|
|
{
|
|
|
|
|
t_oam[s].patterns_0 = BitReverse.Byte8[t_oam[s].patterns_0];
|
|
|
|
|
t_oam[s].patterns_1 = BitReverse.Byte8[t_oam[s].patterns_1];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
2011-06-07 20:41:49 +00:00
|
|
|
|
|
2015-01-17 21:09:33 +00:00
|
|
|
|
} // sprite pattern fetch loop
|
2011-02-27 09:45:50 +00:00
|
|
|
|
|
2017-01-27 23:34:56 +00:00
|
|
|
|
//now do the same for extra sprites, but without any cycles run
|
|
|
|
|
if (bound>8)
|
|
|
|
|
{
|
|
|
|
|
for (int s = 8; s < bound; s++)
|
|
|
|
|
{
|
|
|
|
|
bool junksprite = (!reg_2001.PPUON);
|
|
|
|
|
|
|
|
|
|
t_oam[s].oam_y = soam[s * 4];
|
|
|
|
|
t_oam[s].oam_ind = soam[s * 4 + 1];
|
|
|
|
|
t_oam[s].oam_attr = soam[s * 4 + 2];
|
|
|
|
|
t_oam[s].oam_x = soam[s * 4 + 3];
|
|
|
|
|
|
|
|
|
|
int line = yp - t_oam[s].oam_y;
|
|
|
|
|
if ((t_oam[s].oam_attr & 0x80) != 0) //vflip
|
|
|
|
|
line = spriteHeight - line - 1;
|
|
|
|
|
|
|
|
|
|
int patternNumber = t_oam[s].oam_ind;
|
|
|
|
|
int patternAddress;
|
|
|
|
|
|
|
|
|
|
//8x16 sprite handling:
|
|
|
|
|
if (reg_2000.obj_size_16)
|
|
|
|
|
{
|
|
|
|
|
int bank = (patternNumber & 1) << 12;
|
|
|
|
|
patternNumber = patternNumber & ~1;
|
|
|
|
|
patternNumber |= (line >> 3) & 1;
|
|
|
|
|
patternAddress = (patternNumber << 4) | bank;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
patternAddress = (patternNumber << 4) | (reg_2000.obj_pattern_hi << 12);
|
|
|
|
|
|
|
|
|
|
//offset into the pattern for the current line.
|
|
|
|
|
//tricky: tall sprites have already had lines>8 taken care of by getting a new pattern number above.
|
|
|
|
|
//so we just need the line offset for the second pattern
|
|
|
|
|
patternAddress += line & 7;
|
|
|
|
|
|
|
|
|
|
//garbage nametable fetches + scroll resets
|
|
|
|
|
int garbage_todo = 2;
|
|
|
|
|
|
|
|
|
|
ppubus_read(ppur.get_ntread(), true, false);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < garbage_todo; i++)
|
|
|
|
|
{
|
|
|
|
|
if (i == 0)
|
|
|
|
|
{
|
|
|
|
|
read_value = t_oam[s].oam_y;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
read_value = t_oam[s].oam_ind;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ppubus_read(ppur.get_atread(), true, false); //at or nt?
|
|
|
|
|
|
|
|
|
|
read_value = t_oam[s].oam_attr;
|
|
|
|
|
read_value = t_oam[s].oam_x;
|
|
|
|
|
|
|
|
|
|
int addr = patternAddress;
|
|
|
|
|
t_oam[s].patterns_0 = ppubus_read(addr, true, false);
|
|
|
|
|
|
|
|
|
|
read_value = t_oam[s].oam_x;
|
|
|
|
|
|
|
|
|
|
addr += 8;
|
|
|
|
|
t_oam[s].patterns_1 = ppubus_read(addr, true, false);
|
|
|
|
|
|
|
|
|
|
read_value = t_oam[s].oam_x;
|
|
|
|
|
|
|
|
|
|
// hflip
|
|
|
|
|
if ((t_oam[s].oam_attr & 0x40) == 0)
|
|
|
|
|
{
|
|
|
|
|
t_oam[s].patterns_0 = BitReverse.Byte8[t_oam[s].patterns_0];
|
|
|
|
|
t_oam[s].patterns_1 = BitReverse.Byte8[t_oam[s].patterns_1];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // sprite pattern fetch loop
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-17 21:09:33 +00:00
|
|
|
|
ppuphase = PPUPHASE.BG;
|
2011-02-27 09:45:50 +00:00
|
|
|
|
|
2015-01-17 21:09:33 +00:00
|
|
|
|
// fetch BG: two tiles for next line
|
|
|
|
|
for (int xt = 0; xt < 2; xt++)
|
2016-09-16 11:46:56 +00:00
|
|
|
|
{
|
|
|
|
|
Read_bgdata(ref bgdata[xt]);
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-27 09:45:50 +00:00
|
|
|
|
|
2015-01-17 21:09:33 +00:00
|
|
|
|
// this sequence is tuned to pass 10-even_odd_timing.nes
|
|
|
|
|
runppu(kFetchTime);
|
2016-09-16 11:46:56 +00:00
|
|
|
|
bool evenOddDestiny = (reg_2001.show_bg || reg_2001.show_obj);
|
2015-01-17 21:09:33 +00:00
|
|
|
|
runppu(kFetchTime);
|
2011-06-07 07:14:34 +00:00
|
|
|
|
|
2015-01-17 21:09:33 +00:00
|
|
|
|
// After memory access 170, the PPU simply rests for 4 cycles (or the
|
|
|
|
|
// equivelant of half a memory access cycle) before repeating the whole
|
|
|
|
|
// pixel/scanline rendering process. If the scanline being rendered is the very
|
|
|
|
|
// first one on every second frame, then this delay simply doesn't exist.
|
|
|
|
|
if (sl == 0 && idleSynch && evenOddDestiny && chopdot)
|
|
|
|
|
{ }
|
|
|
|
|
else
|
|
|
|
|
runppu(1);
|
|
|
|
|
} // scanline loop
|
2011-02-27 09:45:50 +00:00
|
|
|
|
|
2015-01-17 21:09:33 +00:00
|
|
|
|
ppur.status.sl = 241;
|
2011-02-27 09:45:50 +00:00
|
|
|
|
|
2015-01-17 21:09:33 +00:00
|
|
|
|
//idle for pre NMI lines
|
|
|
|
|
runppu(preNMIlines * kLineTime);
|
2011-02-27 09:45:50 +00:00
|
|
|
|
|
2015-01-17 21:09:33 +00:00
|
|
|
|
} //FrameAdvance
|
2011-02-27 09:45:50 +00:00
|
|
|
|
|
|
|
|
|
|
2015-01-17 21:09:33 +00:00
|
|
|
|
void FrameAdvance_ppudead()
|
|
|
|
|
{
|
2015-10-13 01:32:06 +00:00
|
|
|
|
//not quite emulating all the NES power up behavior
|
|
|
|
|
//since it is known that the NES ignores writes to some
|
|
|
|
|
//register before around a full frame, but no games
|
|
|
|
|
//should write to those regs during that time, it needs
|
|
|
|
|
//to wait for vblank
|
2017-01-26 00:17:24 +00:00
|
|
|
|
/*
|
|
|
|
|
if (ppudead < 2)
|
|
|
|
|
{
|
|
|
|
|
ppur.status.sl = 241;
|
|
|
|
|
runppu(1);
|
|
|
|
|
Reg2002_vblank_active = true;
|
|
|
|
|
runppu(5);
|
|
|
|
|
runppu(postNMIlines * kLineTime - 6);
|
|
|
|
|
ppur.status.sl = 0;
|
|
|
|
|
clear_2002();
|
|
|
|
|
}
|
2016-10-16 21:25:13 +00:00
|
|
|
|
|
2017-01-26 00:17:24 +00:00
|
|
|
|
if (ppudead==2)
|
|
|
|
|
{
|
|
|
|
|
*/
|
2016-10-16 21:25:13 +00:00
|
|
|
|
runppu(241 * kLineTime-7*3);
|
2017-01-26 00:17:24 +00:00
|
|
|
|
/*
|
|
|
|
|
} else
|
|
|
|
|
{
|
|
|
|
|
runppu(241 * kLineTime);
|
|
|
|
|
runppu(preNMIlines * kLineTime);
|
|
|
|
|
idleSynch ^= true;
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
ppudead--;
|
2011-02-27 09:45:50 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-09-25 15:52:12 +00:00
|
|
|
|
}
|