This commit is contained in:
zeromus 2008-08-18 06:33:14 +00:00
parent 5ad3aeeea4
commit 37b36dc8c0
1 changed files with 229 additions and 227 deletions

View File

@ -1805,284 +1805,286 @@ int FCEUX_PPU_Loop(int skip) {
goto finish; goto finish;
} }
//vblank {
PPU_status |= 0x80; //vblank
if(VBlankON) TriggerNMI(); PPU_status |= 0x80;
if(VBlankON) TriggerNMI();
//TRICKY: //TRICKY:
//even though the timing doc says that every scanline is 1364 except for the first dummy scanline, //even though the timing doc says that every scanline is 1364 except for the first dummy scanline,
//i need these to be 1360 in order to get marble madness and pirates! to work. //i need these to be 1360 in order to get marble madness and pirates! to work.
runppu(20*1360); runppu(20*1360);
//no longer in vblank //no longer in vblank
PPU_status &= ~0x80; PPU_status &= ~0x80;
//fceu cleared the spritehit and spriteoverflow flags at the same time.. it is reasonable here. or, it is resaonable at vblank interrupt time //fceu cleared the spritehit and spriteoverflow flags at the same time.. it is reasonable here. or, it is resaonable at vblank interrupt time
PPU_status &= ~0x40; PPU_status &= ~0x40;
PPU_status &= ~0x20; PPU_status &= ~0x20;
//---------- //----------
//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) { //if(!PPUON) {
// runppu(kLineTime*242); // runppu(kLineTime*242);
// goto finish; // goto finish;
//} //}
//---------- //----------
//There are 2 conditions that update all 5 PPU scroll counters with the //There are 2 conditions that update all 5 PPU scroll counters with the
//contents of the latches adjacent to them. The first is after a write to //contents of the latches adjacent to them. The first is after a write to
//2006/2. The second, is at the beginning of scanline 20, when the PPU starts //2006/2. The second, is at the beginning of scanline 20, when the PPU starts
//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][8][7]; uint8 oams[2][8][7];
int oamcounts[2]={0,0}; int oamcounts[2]={0,0};
int oamslot=0; int oamslot=0;
int oamcount; int oamcount;
//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; int yp = sl-1;
if(sl != 0) {
FCEUD_UpdatePPUView(scanline=yp,1);
FCEUD_UpdateNTView(scanline=yp,1);
}
//twiddle the oam buffers
int scanslot = oamslot^1;
int renderslot = oamslot;
oamslot ^= 1;
oamcount = oamcounts[renderslot];
//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.
for(int xt=0;xt<32;xt++) {
bgdata.main[xt+2].Read(true);
//ok, we're also going to draw here.
//unless we're on the first dummy scanline
if(sl != 0) { if(sl != 0) {
int xstart = xt<<3; FCEUD_UpdatePPUView(scanline=yp,1);
oamcount = oamcounts[renderslot]; FCEUD_UpdateNTView(scanline=yp,1);
uint8 *target=XBuf+(yp<<8)+xstart; }
uint8 *ptr = target;
int rasterpos = xstart;
//check all the conditions that can cause things to render in these 8px //twiddle the oam buffers
bool renderspritenow = SpriteON && rendersprites && (xt>0 || SpriteLeft8); int scanslot = oamslot^1;
bool renderbgnow = ScreenON && renderbg && (xt>0 || BGLeft8); int renderslot = oamslot;
oamslot ^= 1;
for(int xp=0;xp<8;xp++,rasterpos++) { oamcount = oamcounts[renderslot];
//bg pos is different from raster pos due to its offsetability. //the main scanline rendering loop:
//so adjust for that here //32 times, we will fetch a tile and then render 8 pixels.
int bgpos = rasterpos + xscroll; //two of those tiles were read in the last scanline.
int bgpx = bgpos&7; for(int xt=0;xt<32;xt++) {
int bgtile = bgpos>>3; bgdata.main[xt+2].Read(true);
uint8 pixel=0, pixelcolor; //ok, we're also going to draw here.
//unless we're on the first dummy scanline
if(sl != 0) {
int xstart = xt<<3;
oamcount = oamcounts[renderslot];
uint8 *target=XBuf+(yp<<8)+xstart;
uint8 *ptr = target;
int rasterpos = xstart;
//generate the BG data //check all the conditions that can cause things to render in these 8px
if(renderbgnow) bool renderspritenow = SpriteON && rendersprites && (xt>0 || SpriteLeft8);
{ bool renderbgnow = ScreenON && renderbg && (xt>0 || BGLeft8);
uint8* pt = bgdata.main[bgtile].pt;
pixel = ((pt[0]>>(7-bgpx))&1) | (((pt[1]>>(7-bgpx))&1)<<1) | bgdata.main[bgtile].at;
}
pixelcolor = PALRAM[pixel];
//look for a sprite to be drawn for(int xp=0;xp<8;xp++,rasterpos++) {
if(renderspritenow) {
bool havepixel = false;
for(int s=0;s<oamcount;s++) {
uint8* oam = oams[renderslot][s];
int x = oam[3];
if(rasterpos>=x && rasterpos<x+8) {
//build the pixel.
//fetch the LSB of the patterns
uint8 spixel = oam[4]&1;
spixel |= (oam[5]&1)<<1;
//shift down the patterns so the next pixel is in the LSB //bg pos is different from raster pos due to its offsetability.
oam[4] >>= 1; //so adjust for that here
oam[5] >>= 1; int bgpos = rasterpos + xscroll;
int bgpx = bgpos&7;
int bgtile = bgpos>>3;
//bail out if we already have a pixel from a higher priority sprite uint8 pixel=0, pixelcolor;
if(havepixel) continue;
//transparent pixel bailout //generate the BG data
if(spixel==0) continue; if(renderbgnow)
{
uint8* pt = bgdata.main[bgtile].pt;
pixel = ((pt[0]>>(7-bgpx))&1) | (((pt[1]>>(7-bgpx))&1)<<1) | bgdata.main[bgtile].at;
}
pixelcolor = PALRAM[pixel];
//spritehit: //look for a sprite to be drawn
//1. is it sprite#0? if(renderspritenow) {
//2. is the bg pixel nonzero? bool havepixel = false;
//then, it is spritehit. for(int s=0;s<oamcount;s++) {
if(oam[6] == 0 && pixel != 0) uint8* oam = oams[renderslot][s];
PPU_status |= 0x40; int x = oam[3];
if(rasterpos>=x && rasterpos<x+8) {
//build the pixel.
//fetch the LSB of the patterns
uint8 spixel = oam[4]&1;
spixel |= (oam[5]&1)<<1;
havepixel = true; //shift down the patterns so the next pixel is in the LSB
oam[4] >>= 1;
oam[5] >>= 1;
//priority handling //bail out if we already have a pixel from a higher priority sprite
if(oam[2]&0x20) { if(havepixel) continue;
//behind background:
if((pixel&3)!=0) continue; //transparent pixel bailout
if(spixel==0) continue;
//spritehit:
//1. is it sprite#0?
//2. is the bg pixel nonzero?
//then, it is spritehit.
if(oam[6] == 0 && pixel != 0)
PPU_status |= 0x40;
havepixel = true;
//priority handling
if(oam[2]&0x20) {
//behind background:
if((pixel&3)!=0) continue;
}
//bring in the palette bits and palettize
spixel |= (oam[2]&3)<<2;
pixelcolor = PALRAM[0x10+spixel];
} }
//bring in the palette bits and palettize
spixel |= (oam[2]&3)<<2;
pixelcolor = PALRAM[0x10+spixel];
} }
} }
//fceu rendering system requires that this be set
//(so that it knows there is a valid pixel there?)
pixelcolor |= 0x80;
*ptr++ = pixelcolor;
}
}
}
//look for sprites (was supposed to run concurrent with bg rendering)
oamcounts[scanslot] = 0;
oamcount=0;
int spriteHeight = Sprite16?16:8;
for(int i=0;i<64;i++) {
uint8* spr = SPRAM+i*4;
if(yp >= spr[0] && yp < spr[0]+spriteHeight) {
//if we already have 8 sprites, then this new one causes an overflow,
//set the flag and bail out.
if(oamcount == 8) {
PPU_status |= 0x20;
break;
} }
//fceu rendering system requires that this be set //just copy some bytes into the internal sprite buffer
//(so that it knows there is a valid pixel there?) for(int j=0;j<4;j++)
pixelcolor |= 0x80; oams[scanslot][oamcount][j] = spr[j];
*ptr++ = pixelcolor;
//note that we stuff the oam index into [6].
//i need to turn this into a struct so we can have fewer magic numbers
oams[scanslot][oamcount][6] = (uint8)i;
oamcount++;
} }
} }
} oamcounts[scanslot] = oamcount;
//look for sprites (was supposed to run concurrent with bg rendering) //todo - think about clearing oams to a predefined value to force deterministic behavior
oamcounts[scanslot] = 0;
oamcount=0; //fetch sprite patterns
int spriteHeight = Sprite16?16:8; for(int s=0;s<8;s++) {
for(int i=0;i<64;i++) {
uint8* spr = SPRAM+i*4; if(s==1 && sl != 0) {
if(yp >= spr[0] && yp < spr[0]+spriteHeight) { //begin hblank!
//if we already have 8 sprites, then this new one causes an overflow, //the screen is always behind the ppu operation so we need to wait a little longer before triggering it
//set the flag and bail out. //NOTE: SMB3 is very sensitive about this timing, for the statusbar
if(oamcount == 8) { if(PPUON) {
PPU_status |= 0x20; if(GameHBIRQHook)
break; GameHBIRQHook();
} else {
int zzz=9;
}
} }
//just copy some bytes into the internal sprite buffer uint8* oam = oams[scanslot][s];
for(int j=0;j<4;j++) uint32 line = yp - oam[0];
oams[scanslot][oamcount][j] = spr[j]; if(oam[2]&0x80) //vflip
line = spriteHeight-line-1;
//note that we stuff the oam index into [6]. uint32 patternNumber = oam[1];
//i need to turn this into a struct so we can have fewer magic numbers uint32 patternAddress;
oams[scanslot][oamcount][6] = (uint8)i;
oamcount++;
}
}
oamcounts[scanslot] = oamcount;
//todo - think about clearing oams to a predefined value to force deterministic behavior //8x16 sprite handling:
if(Sprite16) {
//fetch sprite patterns uint32 bank = (patternNumber&1)<<12;
for(int s=0;s<8;s++) { patternNumber = patternNumber&~1;
patternNumber |= (line>>3);
if(s==1 && sl != 0) { patternAddress = (patternNumber<<4) | bank;
//begin hblank!
//the screen is always behind the ppu operation so we need to wait a little longer before triggering it
//NOTE: SMB3 is very sensitive about this timing, for the statusbar
if(PPUON) {
if(GameHBIRQHook)
GameHBIRQHook();
} else { } else {
int zzz=9; patternAddress = (patternNumber<<4) | (SpAdrHI<<9);
}
//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
runppu(kFetchTime);
runppu(kFetchTime);
//pattern table fetches
RefreshAddr = patternAddress;
oam[4] = FFCEUX_PPURead(RefreshAddr);
runppu(kFetchTime);
RefreshAddr += 8;
oam[5] = FFCEUX_PPURead(RefreshAddr);
runppu(kFetchTime);
//hflip
if(!(oam[2]&0x40)) {
oam[4] = bitrevlut[oam[4]];
oam[5] = bitrevlut[oam[5]];
} }
} }
uint8* oam = oams[scanslot][s]; // FV is clocked by the PPU's horizontal blanking impulse, and therefore will increment every scanline.
uint32 line = yp - oam[0]; //well, according to my tests, maybe at the end of hblank.
if(oam[2]&0x80) //vflip //but where is the end of hblank?
line = spriteHeight-line-1; //well, i imagine it needs to be before the BG fetches for the next line.
if(PPUON && sl != 0)
ppur.increment_vs();
uint32 patternNumber = oam[1]; //so.. this is the end of hblank. latch horizontal scroll values
uint32 patternAddress; if(PPUON && sl != 0)
ppur.install_h_latches();
//8x16 sprite handling: //capture the next xscroll
if(Sprite16) { xscroll = ppur.fh;
uint32 bank = (patternNumber&1)<<12;
patternNumber = patternNumber&~1;
patternNumber |= (line>>3);
patternAddress = (patternNumber<<4) | bank;
} else {
patternAddress = (patternNumber<<4) | (SpAdrHI<<9);
}
//offset into the pattern for the current line. //fetch BG: two tiles for next line
//tricky: tall sprites have already had lines>8 taken care of by getting a new pattern number above. for(int xt=0;xt<2;xt++)
//so we just need the line offset for the second pattern bgdata.main[xt].Read(true);
patternAddress += line&7;
//garbage nametable fetches //I'm unclear of the reason why this particular access to memory is made.
//The nametable address that is accessed 2 times in a row here, is also the
//same nametable address that points to the 3rd tile to be rendered on the
//screen (or basically, the first nametable address that will be accessed when
//the PPU is fetching background data on the next scanline).
//(not implemented yet)
runppu(kFetchTime); runppu(kFetchTime);
runppu(kFetchTime); runppu(kFetchTime);
//pattern table fetches
RefreshAddr = patternAddress;
oam[4] = FFCEUX_PPURead(RefreshAddr);
runppu(kFetchTime);
RefreshAddr += 8;
oam[5] = FFCEUX_PPURead(RefreshAddr);
runppu(kFetchTime);
//hflip //After memory access 170, the PPU simply rests for 4 cycles (or the
if(!(oam[2]&0x40)) { //equivelant of half a memory access cycle) before repeating the whole
oam[4] = bitrevlut[oam[4]]; //pixel/scanline rendering process. If the scanline being rendered is the very
oam[5] = bitrevlut[oam[5]]; //first one on every second frame, then this delay simply doesn't exist.
} if(sl==0 && idleSynch==0)
{}
else
runppu(4);
} }
// FV is clocked by the PPU's horizontal blanking impulse, and therefore will increment every scanline. idleSynch ++;
//well, according to my tests, maybe at the end of hblank. if(idleSynch==2) idleSynch = 0;
//but where is the end of hblank?
//well, i imagine it needs to be before the BG fetches for the next line.
if(PPUON && sl != 0)
ppur.increment_vs();
//so.. this is the end of hblank. latch horizontal scroll values //idle for one line
if(PPUON && sl != 0) //why 1360? see the note up top at vblank
ppur.install_h_latches(); runppu(1360);
//capture the next xscroll framectr++;
xscroll = ppur.fh;
//fetch BG: two tiles for next line
for(int xt=0;xt<2;xt++)
bgdata.main[xt].Read(true);
//I'm unclear of the reason why this particular access to memory is made.
//The nametable address that is accessed 2 times in a row here, is also the
//same nametable address that points to the 3rd tile to be rendered on the
//screen (or basically, the first nametable address that will be accessed when
//the PPU is fetching background data on the next scanline).
//(not implemented yet)
runppu(kFetchTime);
runppu(kFetchTime);
//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==0)
{}
else
runppu(4);
} }
idleSynch ++;
if(idleSynch==2) idleSynch = 0;
//idle for one line
//why 1360? see the note up top at vblank
runppu(1360);
framectr++;
finish: finish:
FCEU_PutImage(); FCEU_PutImage();