Experimental $2004 implemented and some timing fixed, please test out.
This commit is contained in:
parent
7166219de2
commit
159b18ef79
350
src/ppu.cpp
350
src/ppu.cpp
|
@ -100,6 +100,25 @@ struct BITREVLUT {
|
||||||
};
|
};
|
||||||
BITREVLUT<uint8,8> bitrevlut;
|
BITREVLUT<uint8,8> bitrevlut;
|
||||||
|
|
||||||
|
struct PPUSTATUS
|
||||||
|
{
|
||||||
|
int cycle, end_cycle;
|
||||||
|
int sl;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SPRITE_READ
|
||||||
|
{
|
||||||
|
int num;
|
||||||
|
int count;
|
||||||
|
int fetch;
|
||||||
|
int found;
|
||||||
|
int found_pos[8];
|
||||||
|
int ret;
|
||||||
|
int last;
|
||||||
|
int mode;
|
||||||
|
};
|
||||||
|
struct SPRITE_READ spr_read = { 0 };
|
||||||
|
|
||||||
//uses the internal counters concept at http://nesdev.icequake.net/PPU%20addressing.txt
|
//uses the internal counters concept at http://nesdev.icequake.net/PPU%20addressing.txt
|
||||||
struct PPUREGS {
|
struct PPUREGS {
|
||||||
uint32 fv;//3
|
uint32 fv;//3
|
||||||
|
@ -114,10 +133,14 @@ struct PPUREGS {
|
||||||
|
|
||||||
uint32 _fv, _v, _h, _vt, _ht;
|
uint32 _fv, _v, _h, _vt, _ht;
|
||||||
|
|
||||||
|
struct PPUSTATUS status;
|
||||||
|
|
||||||
PPUREGS()
|
PPUREGS()
|
||||||
: fv(0), v(0), h(0), vt(0), ht(0), fh(0), s(0), par(0), ar(0)
|
: fv(0), v(0), h(0), vt(0), ht(0), fh(0), s(0), par(0), ar(0)
|
||||||
, _fv(0), _v(0), _h(0), _vt(0), _ht(0)
|
, _fv(0), _v(0), _h(0), _vt(0), _ht(0)
|
||||||
{}
|
{ status.cycle = 0; status.end_cycle = 341;
|
||||||
|
status.sl = 241;
|
||||||
|
}
|
||||||
|
|
||||||
void install_latches() {
|
void install_latches() {
|
||||||
fv = _fv;
|
fv = _fv;
|
||||||
|
@ -442,6 +465,200 @@ static DECLFR(A2002)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static DECLFR(A2004)
|
||||||
|
{
|
||||||
|
if (newppu)
|
||||||
|
{
|
||||||
|
if (0)
|
||||||
|
//if ((ppur.status.sl < 241) && PPUON)
|
||||||
|
{
|
||||||
|
/* from cycles 0 to 63, the
|
||||||
|
* 32 byte OAM buffer gets init
|
||||||
|
* to 0xFF */
|
||||||
|
if (ppur.status.cycle < 64)
|
||||||
|
return spr_read.ret = 0xFF;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int i = spr_read.last;
|
||||||
|
i != ppur.status.cycle; ++i)
|
||||||
|
{
|
||||||
|
if (i < 256)
|
||||||
|
{
|
||||||
|
switch (spr_read.mode)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
if (spr_read.count < 2)
|
||||||
|
spr_read.ret = (PPU[3] & 0xF8)
|
||||||
|
+ (spr_read.count << 2);
|
||||||
|
else
|
||||||
|
spr_read.ret = spr_read.count << 2;
|
||||||
|
spr_read.found_pos[spr_read.found] = spr_read.ret;
|
||||||
|
|
||||||
|
spr_read.ret = SPRAM[spr_read.ret];
|
||||||
|
|
||||||
|
if (i & 1) //odd cycle
|
||||||
|
{
|
||||||
|
//see if in range
|
||||||
|
if ( (ppur.status.sl - 1 - spr_read.ret)
|
||||||
|
& ~(Sprite16 ? 0xF : 0x7) )
|
||||||
|
|
||||||
|
{
|
||||||
|
++spr_read.found;
|
||||||
|
spr_read.fetch = 1;
|
||||||
|
spr_read.mode = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (++spr_read.count == 64)
|
||||||
|
{
|
||||||
|
spr_read.mode = 4;
|
||||||
|
spr_read.count = 0;
|
||||||
|
}
|
||||||
|
else if (spr_read.found == 8)
|
||||||
|
{
|
||||||
|
spr_read.fetch = 0;
|
||||||
|
spr_read.mode = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 1: //sprite is in range fetch the next 4 bytes
|
||||||
|
if (i & 1)
|
||||||
|
{
|
||||||
|
++spr_read.fetch;
|
||||||
|
if (spr_read.fetch == 4)
|
||||||
|
{
|
||||||
|
spr_read.fetch = 1;
|
||||||
|
if (++spr_read.count == 64)
|
||||||
|
{
|
||||||
|
spr_read.count = 0;
|
||||||
|
spr_read.mode = 4;
|
||||||
|
}
|
||||||
|
else if (spr_read.found == 8)
|
||||||
|
{
|
||||||
|
spr_read.fetch = 0;
|
||||||
|
spr_read.mode = 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
spr_read.mode = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spr_read.count < 2)
|
||||||
|
spr_read.ret = (PPU[3] & 0xF8)
|
||||||
|
+ (spr_read.count << 2);
|
||||||
|
else
|
||||||
|
spr_read.ret = spr_read.count << 2;
|
||||||
|
|
||||||
|
spr_read.ret = SPRAM[spr_read.ret |
|
||||||
|
spr_read.fetch];
|
||||||
|
break;
|
||||||
|
case 2: //8th sprite fetched
|
||||||
|
spr_read.ret = SPRAM[(spr_read.count << 2)
|
||||||
|
| spr_read.fetch];
|
||||||
|
if (i & 1)
|
||||||
|
{
|
||||||
|
if ( (ppur.status.sl - 1 -
|
||||||
|
SPRAM[(spr_read.count << 2) |
|
||||||
|
spr_read.fetch])
|
||||||
|
& ~(Sprite16 ? 0xF : 0x7) )
|
||||||
|
{
|
||||||
|
spr_read.fetch = 1;
|
||||||
|
spr_read.mode = 3;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (++spr_read.count == 64)
|
||||||
|
{
|
||||||
|
spr_read.count = 0;
|
||||||
|
spr_read.mode = 4;
|
||||||
|
}
|
||||||
|
spr_read.fetch = (spr_read.fetch + 1) & 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spr_read.ret = spr_read.count;
|
||||||
|
break;
|
||||||
|
case 3: //9th sprite overflow detected
|
||||||
|
spr_read.ret = SPRAM[spr_read.count | spr_read.fetch];
|
||||||
|
if (i & 1)
|
||||||
|
{
|
||||||
|
if (++spr_read.fetch == 4)
|
||||||
|
{
|
||||||
|
spr_read.count = (spr_read.count + 1) & 63;
|
||||||
|
spr_read.mode = 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 4: //read OAM[n][0] until hblank
|
||||||
|
if (i & 1)
|
||||||
|
spr_read.count = (spr_read.count + 1) & 63;
|
||||||
|
spr_read.fetch = 0;
|
||||||
|
spr_read.ret = SPRAM[spr_read.count << 2];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (i < 320)
|
||||||
|
{
|
||||||
|
spr_read.ret = (i & 0x38) >> 3;
|
||||||
|
if (spr_read.found < (spr_read.ret + 1))
|
||||||
|
{
|
||||||
|
if (spr_read.num)
|
||||||
|
{
|
||||||
|
spr_read.ret = SPRAM[252];
|
||||||
|
spr_read.num = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
spr_read.ret = 0xFF;
|
||||||
|
}
|
||||||
|
else if ((i & 7) < 4)
|
||||||
|
{
|
||||||
|
spr_read.ret = SPRAM[spr_read.found_pos[spr_read.ret]
|
||||||
|
| spr_read.fetch++];
|
||||||
|
if (spr_read.fetch == 4)
|
||||||
|
spr_read.fetch = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
spr_read.ret = SPRAM[spr_read.found_pos[spr_read.ret | 3]];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!spr_read.found)
|
||||||
|
spr_read.ret = SPRAM[252];
|
||||||
|
else
|
||||||
|
spr_read.ret = SPRAM[spr_read.found_pos[0]];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spr_read.last = ppur.status.cycle;
|
||||||
|
return spr_read.ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//hack from nintendulator to get micro machines working properly until
|
||||||
|
//the per cycle thing is debugged more
|
||||||
|
//thanks!
|
||||||
|
else if ((ppur.status.sl < 241) && PPUON)
|
||||||
|
{
|
||||||
|
if (ppur.status.cycle < 64)
|
||||||
|
return 0xFF;
|
||||||
|
else if (ppur.status.cycle < 192)
|
||||||
|
return SPRAM[((ppur.status.cycle - 64) << 1) & 0xFC];
|
||||||
|
else if (ppur.status.cycle < 256)
|
||||||
|
return (ppur.status.cycle & 1) ? SPRAM[0xFC] : SPRAM[((ppur.status.cycle - 192) << 1) & 0xFC];
|
||||||
|
else if (ppur.status.cycle < 320)
|
||||||
|
return 0xFF;
|
||||||
|
else
|
||||||
|
return SPRAM[0];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return SPRAM[PPU[3]];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
FCEUPPU_LineUpdate();
|
||||||
|
return PPUGenLatch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static DECLFR(A200x) /* Not correct for $2004 reads. */
|
static DECLFR(A200x) /* Not correct for $2004 reads. */
|
||||||
{
|
{
|
||||||
FCEUPPU_LineUpdate();
|
FCEUPPU_LineUpdate();
|
||||||
|
@ -568,7 +785,7 @@ static DECLFW(B2001)
|
||||||
if(V&0xE0)
|
if(V&0xE0)
|
||||||
deemp=V>>5;
|
deemp=V>>5;
|
||||||
}
|
}
|
||||||
|
//
|
||||||
static DECLFW(B2002)
|
static DECLFW(B2002)
|
||||||
{
|
{
|
||||||
PPUGenLatch=V;
|
PPUGenLatch=V;
|
||||||
|
@ -585,8 +802,19 @@ static DECLFW(B2003)
|
||||||
static DECLFW(B2004)
|
static DECLFW(B2004)
|
||||||
{
|
{
|
||||||
//printf("Wr: %04x:$%02x\n",A,V);
|
//printf("Wr: %04x:$%02x\n",A,V);
|
||||||
|
|
||||||
PPUGenLatch=V;
|
PPUGenLatch=V;
|
||||||
|
if (newppu)
|
||||||
|
{
|
||||||
|
//the attribute upper bits are not connected
|
||||||
|
//so AND them out on write, since reading them
|
||||||
|
//should return 0 in those bits.
|
||||||
|
if ((PPU[3] & 3) == 2)
|
||||||
|
V &= 0xE3;
|
||||||
|
SPRAM[PPU[3]] = V;
|
||||||
|
PPU[3] = (PPU[3] + 1) & 0xFF;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
if(PPUSPL>=8)
|
if(PPUSPL>=8)
|
||||||
{
|
{
|
||||||
if(PPU[3]>=8)
|
if(PPU[3]>=8)
|
||||||
|
@ -599,7 +827,7 @@ static DECLFW(B2004)
|
||||||
}
|
}
|
||||||
PPU[3]++;
|
PPU[3]++;
|
||||||
PPUSPL++;
|
PPUSPL++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static DECLFW(B2005)
|
static DECLFW(B2005)
|
||||||
|
@ -1568,7 +1796,6 @@ void FCEUPPU_Reset(void)
|
||||||
vtoggle = 0;
|
vtoggle = 0;
|
||||||
ppudead = 2;
|
ppudead = 2;
|
||||||
kook = 0;
|
kook = 0;
|
||||||
|
|
||||||
// XOffset=0;
|
// XOffset=0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1592,7 +1819,7 @@ void FCEUPPU_Power(void)
|
||||||
BWrite[x+2]=B2002;
|
BWrite[x+2]=B2002;
|
||||||
ARead[x+3]=A200x;
|
ARead[x+3]=A200x;
|
||||||
BWrite[x+3]=B2003;
|
BWrite[x+3]=B2003;
|
||||||
ARead[x+4]=A200x; //A2004;
|
ARead[x+4]=A2004; //A2004;
|
||||||
BWrite[x+4]=B2004;
|
BWrite[x+4]=B2004;
|
||||||
ARead[x+5]=A200x;
|
ARead[x+5]=A200x;
|
||||||
BWrite[x+5]=B2005;
|
BWrite[x+5]=B2005;
|
||||||
|
@ -1783,11 +2010,13 @@ int pputime=0;
|
||||||
int totpputime=0;
|
int totpputime=0;
|
||||||
const int kLineTime=341;
|
const int kLineTime=341;
|
||||||
const int kFetchTime=2;
|
const int kFetchTime=2;
|
||||||
int idleSynch = 0;
|
int idleSynch = 1;
|
||||||
|
|
||||||
void runppu(int x) {
|
void runppu(int x) {
|
||||||
//pputime+=x;
|
//pputime+=x;
|
||||||
//if(cputodo<200) return;
|
//if(cputodo<200) return;
|
||||||
|
ppur.status.cycle = (ppur.status.cycle + x) %
|
||||||
|
ppur.status.end_cycle;
|
||||||
X6502_Run(x);
|
X6502_Run(x);
|
||||||
//pputime -= cputodo<<2;
|
//pputime -= cputodo<<2;
|
||||||
}
|
}
|
||||||
|
@ -1804,13 +2033,22 @@ struct BGData {
|
||||||
|
|
||||||
RefreshAddr = ppur.get_atread();
|
RefreshAddr = ppur.get_atread();
|
||||||
at = CALL_PPUREAD(RefreshAddr);
|
at = CALL_PPUREAD(RefreshAddr);
|
||||||
runppu(kFetchTime);
|
|
||||||
|
|
||||||
//modify at to get appropriate palette shift
|
//modify at to get appropriate palette shift
|
||||||
if(ppur.vt&2) at >>= 4;
|
if(ppur.vt&2) at >>= 4;
|
||||||
if(ppur.ht&2) at >>= 2;
|
if(ppur.ht&2) at >>= 2;
|
||||||
at &= 0x03;
|
at &= 0x03;
|
||||||
at <<= 2;
|
at <<= 2;
|
||||||
|
//horizontal scroll clocked at cycle 3 and then
|
||||||
|
//vertical scroll at 251
|
||||||
|
runppu(1);
|
||||||
|
if (PPUON)
|
||||||
|
{
|
||||||
|
ppur.increment_hsc();
|
||||||
|
if (ppur.status.cycle == 251)
|
||||||
|
ppur.increment_vs();
|
||||||
|
}
|
||||||
|
runppu(1);
|
||||||
|
|
||||||
ppur.par = nt;
|
ppur.par = nt;
|
||||||
RefreshAddr = ppur.get_ptread();
|
RefreshAddr = ppur.get_ptread();
|
||||||
|
@ -1819,9 +2057,6 @@ struct BGData {
|
||||||
RefreshAddr |= 8;
|
RefreshAddr |= 8;
|
||||||
pt[1] = CALL_PPUREAD(RefreshAddr);
|
pt[1] = CALL_PPUREAD(RefreshAddr);
|
||||||
runppu(kFetchTime);
|
runppu(kFetchTime);
|
||||||
|
|
||||||
if(PPUON)
|
|
||||||
ppur.increment_hsc();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1832,12 +2067,21 @@ struct BGData {
|
||||||
int framectr=0;
|
int framectr=0;
|
||||||
int FCEUX_PPU_Loop(int skip) {
|
int FCEUX_PPU_Loop(int skip) {
|
||||||
//262 scanlines
|
//262 scanlines
|
||||||
|
|
||||||
if (ppudead)
|
if (ppudead)
|
||||||
{
|
{
|
||||||
memset(XBuf, 0x80, 256*240);
|
/* not quite emulating all the NES power up behavior
|
||||||
runppu(262*kLineTime);
|
* since it is known that the NES ignores writes to some
|
||||||
ppudead--;
|
* register before around a full frame, but no games
|
||||||
|
* should write to those regs during that time, it needs
|
||||||
|
* to wait for vblank */
|
||||||
|
ppur.status.sl = 241;
|
||||||
|
if (PAL)
|
||||||
|
runppu(70*kLineTime);
|
||||||
|
else
|
||||||
|
runppu(20*kLineTime);
|
||||||
|
ppur.status.sl = 0;
|
||||||
|
runppu(242*kLineTime);
|
||||||
|
ppudead = 0;
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1848,16 +2092,16 @@ int FCEUX_PPU_Loop(int skip) {
|
||||||
//Not sure if this is correct. According to Matt Conte and my own tests, it is.
|
//Not sure if this is correct. According to Matt Conte and my own tests, it is.
|
||||||
//Timing is probably off, though.
|
//Timing is probably off, though.
|
||||||
//NOTE: Not having this here breaks a Super Donkey Kong game.
|
//NOTE: Not having this here breaks a Super Donkey Kong game.
|
||||||
//PPU[3]=PPUSPL=0;
|
PPU[3]=PPUSPL=0;
|
||||||
|
|
||||||
const int delay = 20; //fceu used 12 here but I couldnt get it to work in marble madness and pirates.
|
const int delay = 20; //fceu used 12 here but I couldnt get it to work in marble madness and pirates.
|
||||||
|
|
||||||
|
ppur.status.sl = 241; //for sprite reads
|
||||||
|
|
||||||
runppu(delay); //X6502_Run(12);
|
runppu(delay); //X6502_Run(12);
|
||||||
if(VBlankON) TriggerNMI();
|
if(VBlankON) TriggerNMI();
|
||||||
runppu(20*(kLineTime)-delay);
|
runppu(20*(kLineTime)-delay);
|
||||||
|
|
||||||
//this seems to run just before the dummy scanline begins
|
//this seems to run just before the dummy scanline begins
|
||||||
PPU_status&=0x1f;
|
PPU_status = 0;
|
||||||
|
|
||||||
//this early out caused metroid to fail to boot. I am leaving it here as a reminder of what not to do
|
//this early out caused metroid to fail to boot. I am leaving it here as a reminder of what not to do
|
||||||
//if(!PPUON) { runppu(kLineTime*242); goto finish; }
|
//if(!PPUON) { runppu(kLineTime*242); goto finish; }
|
||||||
|
|
||||||
|
@ -1867,8 +2111,8 @@ int FCEUX_PPU_Loop(int skip) {
|
||||||
//rendering data for the first time in a frame (this update won't happen if
|
//rendering data for the first time in a frame (this update won't happen if
|
||||||
//all rendering is disabled via 2001.3 and 2001.4).
|
//all rendering is disabled via 2001.3 and 2001.4).
|
||||||
|
|
||||||
if(PPUON)
|
//if(PPUON)
|
||||||
ppur.install_latches();
|
// ppur.install_latches();
|
||||||
|
|
||||||
uint8 oams[2][64][7];
|
uint8 oams[2][64][7];
|
||||||
int oamcounts[2]={0,0};
|
int oamcounts[2]={0,0};
|
||||||
|
@ -1877,11 +2121,19 @@ int FCEUX_PPU_Loop(int skip) {
|
||||||
|
|
||||||
//capture the initial xscroll
|
//capture the initial xscroll
|
||||||
//int xscroll = ppur.fh;
|
//int xscroll = ppur.fh;
|
||||||
|
|
||||||
//render 241 scanlines (including 1 dummy at beginning)
|
//render 241 scanlines (including 1 dummy at beginning)
|
||||||
for(int sl=0;sl<241;sl++) {
|
for(int sl=0;sl<241;sl++) {
|
||||||
int yp = sl-1;
|
spr_read.num = 1;
|
||||||
|
spr_read.found = 0;
|
||||||
|
spr_read.fetch = 1;
|
||||||
|
spr_read.count = 0;
|
||||||
|
spr_read.last = 64;
|
||||||
|
spr_read.mode = 0;
|
||||||
|
memset(spr_read.found_pos, 0, sizeof(spr_read.found_pos));
|
||||||
|
|
||||||
|
ppur.status.sl = sl;
|
||||||
|
|
||||||
|
int yp = sl-1;
|
||||||
ppuphase = PPUPHASE_BG;
|
ppuphase = PPUPHASE_BG;
|
||||||
|
|
||||||
if(sl != 0) {
|
if(sl != 0) {
|
||||||
|
@ -1917,7 +2169,6 @@ int FCEUX_PPU_Loop(int skip) {
|
||||||
//check all the conditions that can cause things to render in these 8px
|
//check all the conditions that can cause things to render in these 8px
|
||||||
bool renderspritenow = SpriteON && rendersprites && (xt>0 || SpriteLeft8);
|
bool renderspritenow = SpriteON && rendersprites && (xt>0 || SpriteLeft8);
|
||||||
bool renderbgnow = ScreenON && renderbg && (xt>0 || BGLeft8);
|
bool renderbgnow = ScreenON && renderbg && (xt>0 || BGLeft8);
|
||||||
|
|
||||||
for(int xp=0;xp<8;xp++,rasterpos++) {
|
for(int xp=0;xp<8;xp++,rasterpos++) {
|
||||||
|
|
||||||
//bg pos is different from raster pos due to its offsetability.
|
//bg pos is different from raster pos due to its offsetability.
|
||||||
|
@ -2017,12 +2268,17 @@ int FCEUX_PPU_Loop(int skip) {
|
||||||
//FV is clocked by the PPU's horizontal blanking impulse, and therefore will increment every scanline.
|
//FV is clocked by the PPU's horizontal blanking impulse, and therefore will increment every scanline.
|
||||||
//well, according to (which?) tests, maybe at the end of hblank.
|
//well, according to (which?) tests, maybe at the end of hblank.
|
||||||
//but, according to what it took to get crystalis working, it is at the beginning of hblank.
|
//but, according to what it took to get crystalis working, it is at the beginning of hblank.
|
||||||
if(PPUON && sl != 0)
|
|
||||||
ppur.increment_vs();
|
//this is done at cycle 251
|
||||||
|
//rendering scanline, it doesn't need to be scanline 0,
|
||||||
|
//because on the first scanline when the increment is 0, the vs_scroll is reloaded.
|
||||||
|
//if(PPUON && sl != 0)
|
||||||
|
// ppur.increment_vs();
|
||||||
|
|
||||||
//todo - think about clearing oams to a predefined value to force deterministic behavior
|
//todo - think about clearing oams to a predefined value to force deterministic behavior
|
||||||
|
|
||||||
//so.. this is the end of hblank. latch horizontal scroll values
|
//so.. this is the end of hblank. latch horizontal scroll values
|
||||||
|
//do it cycle at 251
|
||||||
if(PPUON && sl != 0)
|
if(PPUON && sl != 0)
|
||||||
ppur.install_h_latches();
|
ppur.install_h_latches();
|
||||||
|
|
||||||
|
@ -2065,7 +2321,23 @@ int FCEUX_PPU_Loop(int skip) {
|
||||||
patternAddress += line&7;
|
patternAddress += line&7;
|
||||||
|
|
||||||
//garbage nametable fetches
|
//garbage nametable fetches
|
||||||
if(realSprite) runppu(kFetchTime);
|
//reset the scroll counter, happens at cycle 304
|
||||||
|
if (realSprite)
|
||||||
|
{
|
||||||
|
if ((sl == 0) && PPUON)
|
||||||
|
{
|
||||||
|
if (ppur.status.cycle == 304)
|
||||||
|
{
|
||||||
|
runppu(1);
|
||||||
|
ppur.install_latches();
|
||||||
|
runppu(1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
runppu(kFetchTime);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
runppu(kFetchTime);
|
||||||
|
}
|
||||||
|
|
||||||
if(((PPU[0]&0x38)!=0x18) && s == 2 && SpriteON ) {
|
if(((PPU[0]&0x38)!=0x18) && s == 2 && SpriteON ) {
|
||||||
//(The MMC3 scanline counter is based entirely on PPU A12, triggered on rising edges (after the line remains low for a sufficiently long period of time))
|
//(The MMC3 scanline counter is based entirely on PPU A12, triggered on rising edges (after the line remains low for a sufficiently long period of time))
|
||||||
|
@ -2080,15 +2352,16 @@ int FCEUX_PPU_Loop(int skip) {
|
||||||
|
|
||||||
if(realSprite) runppu(kFetchTime);
|
if(realSprite) runppu(kFetchTime);
|
||||||
|
|
||||||
|
|
||||||
//pattern table fetches
|
//pattern table fetches
|
||||||
RefreshAddr = patternAddress;
|
RefreshAddr = patternAddress;
|
||||||
oam[4] = CALL_PPUREAD(RefreshAddr);
|
oam[4] = CALL_PPUREAD(RefreshAddr);
|
||||||
if(realSprite) runppu(kFetchTime);
|
if(realSprite) runppu(kFetchTime);
|
||||||
|
|
||||||
RefreshAddr += 8;
|
RefreshAddr += 8;
|
||||||
oam[5] = CALL_PPUREAD(RefreshAddr);
|
oam[5] = CALL_PPUREAD(RefreshAddr);
|
||||||
if(realSprite) runppu(kFetchTime);
|
if(realSprite) runppu(kFetchTime);
|
||||||
|
|
||||||
|
|
||||||
//hflip
|
//hflip
|
||||||
if(!(oam[2]&0x40)) {
|
if(!(oam[2]&0x40)) {
|
||||||
oam[4] = bitrevlut[oam[4]];
|
oam[4] = bitrevlut[oam[4]];
|
||||||
|
@ -2109,27 +2382,30 @@ int FCEUX_PPU_Loop(int skip) {
|
||||||
//the PPU is fetching background data on the next scanline).
|
//the PPU is fetching background data on the next scanline).
|
||||||
//(not implemented yet)
|
//(not implemented yet)
|
||||||
runppu(kFetchTime);
|
runppu(kFetchTime);
|
||||||
|
if (sl == 0)
|
||||||
|
{
|
||||||
|
if (idleSynch && PPUON && !PAL)
|
||||||
|
ppur.status.end_cycle = 340;
|
||||||
|
else
|
||||||
|
ppur.status.end_cycle = 341;
|
||||||
|
idleSynch ^= 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ppur.status.end_cycle = 341;
|
||||||
runppu(kFetchTime);
|
runppu(kFetchTime);
|
||||||
|
|
||||||
|
|
||||||
//After memory access 170, the PPU simply rests for 4 cycles (or the
|
//After memory access 170, the PPU simply rests for 4 cycles (or the
|
||||||
//equivelant of half a memory access cycle) before repeating the whole
|
//equivelant of half a memory access cycle) before repeating the whole
|
||||||
//pixel/scanline rendering process. If the scanline being rendered is the very
|
//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.
|
//first one on every second frame, then this delay simply doesn't exist.
|
||||||
if(sl==0 && idleSynch==0)
|
if (ppur.status.end_cycle == 341)
|
||||||
{}
|
|
||||||
else
|
|
||||||
runppu(1);
|
runppu(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
idleSynch ++;
|
|
||||||
if(idleSynch==2) idleSynch = 0;
|
|
||||||
|
|
||||||
if(MMC5Hack && PPUON) MMC5_hb(240);
|
if(MMC5Hack && PPUON) MMC5_hb(240);
|
||||||
|
|
||||||
//idle for one line
|
//idle for one line
|
||||||
runppu(kLineTime);
|
runppu(kLineTime);
|
||||||
|
|
||||||
framectr++;
|
framectr++;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue