098 renderer added
[fceu.git] / ppu098.c
1 /* FCE Ultra - NES/Famicom Emulator
2  *
3  * Copyright notice for this file:
4  *  Copyright (C) 1998 BERO
5  *  Copyright (C) 2003 Xodnizel
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #include  <string.h>
23
24 #include  "types.h"
25 #include  "x6502.h"
26 #include  "fce.h"
27 #include  "ppu098.h"
28 #include  "nsf.h"
29 #include  "sound.h" // TODO 098?
30 #include  "memory.h"
31
32 #include  "cart.h"
33 #include  "palette.h"
34 #include  "video.h"
35 #include  "input.h"
36
37 #define Pal     (PALRAM)
38
39 static void FetchSpriteData098(void);
40 static void FASTAPASS(1) RefreshLine098(int lastpixel);
41 static void RefreshSprites098(void);
42 static void CopySprites098(uint8 *target);
43
44 static void Fixit1(void);
45 static uint32 ppulut1[256];
46 static uint32 ppulut2[256];
47 static uint32 ppulut3[128];
48
49 static void makeppulut(void)
50 {
51  int x;
52  int y;
53
54  for(x=0;x<256;x++)
55  {
56   ppulut1[x]=0;
57   for(y=0;y<8;y++)
58    ppulut1[x]|=((x>>(7-y))&1)<<(y*4);
59   ppulut2[x]=ppulut1[x]<<1;
60  }
61
62  {
63
64   int cc,xo,pixel;
65
66   for(cc=0;cc<16;cc++)
67   {
68    for(xo=0;xo<8;xo++)
69    {
70     ppulut3[xo|(cc<<3)]=0;
71     for(pixel=0;pixel<8;pixel++)
72     {
73      int shiftr;
74       shiftr=(pixel+xo)/8;
75       shiftr*=2;
76       ppulut3[xo|(cc<<3)]|=(( cc>>shiftr )&3)<<(2+pixel*4);
77     }
78 //    printf("%08x\n",ppulut3[xo|(cc<<3)]);
79    }
80   }
81
82  }
83 }
84
85 // TODO: make this compatible with the new sound code
86 #ifdef ASM_6502
87 static void asmcpu_update(int32 cycles)
88 {
89  // some code from x6502.c
90  fhcnt-=cycles;
91  if(fhcnt<=0)
92  {
93   FrameSoundUpdate();
94   fhcnt+=fhinc;
95  }
96
97  if(PCMIRQCount>0)
98  {
99   PCMIRQCount-=cycles;
100   if(PCMIRQCount<=0)
101   {
102    vdis=1;
103    if((PSG[0x10]&0x80) && !(PSG[0x10]&0x40))
104    {
105     extern uint8 SIRQStat;
106     SIRQStat|=0x80;
107     X6502_IRQBegin(FCEU_IQDPCM);
108    }
109   }
110  }
111 }
112 #endif
113
114 extern int ppudead;
115 extern int kook;
116
117 /* Color deemphasis emulation.  Joy... */
118 static uint8 deemp=0;
119 static int deempcnt[8];
120
121 extern int maxsprites;
122
123 extern uint8 PPUSPL;
124 extern uint8 SPRAM[0x100];
125 extern uint8 SPRBUF[0x100];
126
127
128 #define MMC5SPRVRAMADR(V)      &MMC5SPRVPage[(V)>>10][(V)]
129 #define MMC5BGVRAMADR(V)      &MMC5BGVPage[(V)>>10][(V)]
130 #define VRAMADR(V)      &VPage[(V)>>10][(V)]
131
132
133 static DECLFR(A2002)
134 {
135                         uint8 ret;
136
137                         FCEUPPU_LineUpdate098();
138                         ret = PPU_status;
139                         ret|=PPUGenLatch&0x1F;
140
141                         {
142                          vtoggle=0;
143                          PPU_status&=0x7F;
144                          PPUGenLatch=ret;
145                         }
146                         return ret;
147 }
148
149 static DECLFR(A200x)  /* Not correct for $2004 reads. */
150 {
151                         FCEUPPU_LineUpdate098();
152                         return PPUGenLatch;
153 }
154
155 /*
156 static DECLFR(A2004)
157 {
158                         uint8 ret;
159
160                         FCEUPPU_LineUpdate098();
161                         ret = SPRAM[PPU[3]];
162
163                         if(PPUSPL>=8)
164                         {
165                          if(PPU[3]>=8)
166                           ret = SPRAM[PPU[3]];
167                         }
168                         else
169                         {
170                          //printf("$%02x:$%02x\n",PPUSPL,V);
171                          ret = SPRAM[PPUSPL];
172                         }
173                         PPU[3]++;
174                         PPUSPL++;
175                         PPUGenLatch = ret;
176                         printf("%d, %02x\n",scanline,ret);
177                         return(ret);
178 }
179 */
180 static DECLFR(A2007)
181 {
182                         uint8 ret;
183                         uint32 tmp=RefreshAddr&0x3FFF;
184
185                         FCEUPPU_LineUpdate098();
186
187                         ret=VRAMBuffer;
188
189                         {
190                          if(PPU_hook) PPU_hook(tmp);
191                          PPUGenLatch=VRAMBuffer;
192                          if(tmp<0x2000)
193                          {
194                           VRAMBuffer=VPage[tmp>>10][tmp];
195                          }
196                          else
197                          {
198                           VRAMBuffer=vnapage[(tmp>>10)&0x3][tmp&0x3FF];
199                          }
200                         }
201                         {
202        if(INC32) RefreshAddr+=32;
203                          else RefreshAddr++;
204                          if(PPU_hook) PPU_hook(RefreshAddr&0x3fff);
205                         }
206                         return ret;
207 }
208
209 static DECLFW(B2000)
210 {
211 //    FCEU_printf("%04x:%02x, (%d) %02x, %02x\n",A,V,scanline,PPU[0],PPU_status);
212
213                 FCEUPPU_LineUpdate098();
214                 PPUGenLatch=V;
215                 if(!(PPU[0]&0x80) && (V&0x80) && (PPU_status&0x80))
216                 {
217 //     FCEU_printf("Trigger NMI, %d, %d\n",timestamp,ppudead);
218 //                 TriggerNMI2();
219                  TriggerNMI();
220                 }
221                 PPU[0]=V;
222                 TempAddr&=0xF3FF;
223                 TempAddr|=(V&3)<<10;
224 }
225
226 static DECLFW(B2001)
227 {
228                 //printf("%04x:$%02x, %d\n",A,V,scanline);
229                 FCEUPPU_LineUpdate098();
230                 PPUGenLatch=V;
231                 PPU[1]=V;
232                 if(V&0xE0)
233                  deemp=V>>5;
234 }
235
236 static DECLFW(B2002)
237 {
238                 PPUGenLatch=V;
239 }
240
241 static DECLFW(B2003)
242 {
243                 //printf("$%04x:$%02x, %d, %d\n",A,V,timestamp,scanline);
244                 PPUGenLatch=V;
245                 PPU[3]=V;
246                 PPUSPL=V&0x7;
247 }
248
249 static DECLFW(B2004)
250 {
251                 //printf("Wr: %04x:$%02x\n",A,V);
252
253                 PPUGenLatch=V;
254                 if(PPUSPL>=8)
255                 {
256                  if(PPU[3]>=8)
257                   SPRAM[PPU[3]]=V;
258                 }
259                 else
260                 {
261                  //printf("$%02x:$%02x\n",PPUSPL,V);
262                  SPRAM[PPUSPL]=V;
263                 }
264                 PPU[3]++;
265                 PPUSPL++;
266
267 }
268
269 static DECLFW(B2005)
270 {
271                 uint32 tmp=TempAddr;
272                 FCEUPPU_LineUpdate098();
273                 PPUGenLatch=V;
274     if(!vtoggle)
275                 {
276                  tmp&=0xFFE0;
277                  tmp|=V>>3;
278                  XOffset=V&7;
279                 }
280                 else
281                 {
282                  tmp&=0x8C1F;
283                  tmp|=((V&~0x7)<<2);
284                  tmp|=(V&7)<<12;
285                 }
286                 TempAddr=tmp;
287                 vtoggle^=1;
288 }
289
290
291 static DECLFW(B2006)
292 {
293                 FCEUPPU_LineUpdate098();
294
295                 PPUGenLatch=V;
296                 if(!vtoggle)
297                 {
298                  TempAddr&=0x00FF;
299                  TempAddr|=(V&0x3f)<<8;
300                 }
301                 else
302                 {
303                  TempAddr&=0xFF00;
304                  TempAddr|=V;
305
306                  RefreshAddr=TempAddr;
307                  if(PPU_hook)
308                   PPU_hook(RefreshAddr);
309                  //printf("%d, %04x\n",scanline,RefreshAddr);
310                 }
311                 vtoggle^=1;
312 }
313
314 static DECLFW(B2007)
315 {
316                         uint32 tmp=RefreshAddr&0x3FFF;
317                         PPUGenLatch=V;
318                         if(tmp>=0x3F00)
319                         {
320                          // hmmm....
321                          if(!(tmp&0xf))
322                           PALRAM[0x00]=PALRAM[0x04]=PALRAM[0x08]=PALRAM[0x0C]=V&0x3F;
323                          else if(tmp&3) PALRAM[(tmp&0x1f)]=V&0x3f;
324                         }
325                         else if(tmp<0x2000)
326                         {
327                           if(PPUCHRRAM&(1<<(tmp>>10)))
328                             VPage[tmp>>10][tmp]=V;
329                         }
330                         else
331                         {
332                          if(PPUNTARAM&(1<<((tmp&0xF00)>>10)))
333                           vnapage[((tmp&0xF00)>>10)][tmp&0x3FF]=V;
334                         }
335 //      FCEU_printf("ppu (%04x) %04x:%04x %d, %d\n",X.PC,RefreshAddr,PPUGenLatch,scanline,timestamp);
336       if(INC32) RefreshAddr+=32;
337                         else RefreshAddr++;
338                         if(PPU_hook) PPU_hook(RefreshAddr&0x3fff);
339 }
340
341 static DECLFW(B4014)
342 {
343         uint32 t=V<<8;
344         int x;
345
346         //for(x=0;x<256;x++)
347         // X6502_DMW(0x2004,X6502_DMR(t+x));
348         for(x=0;x<256;x++)
349          B2004(0x2004,X.DB=ARead[t+x](t+x));
350         X6502_AddCycles(512);
351 }
352
353 #define PAL(c)  ((c)+cc)
354
355 #define GETLASTPIXEL    (PAL?((timestamp*48-linestartts)/15) : ((timestamp*48-linestartts)>>4) )
356
357 static uint8 *Pline=0,*Plinef;
358 static int firsttile;
359 static int linestartts;
360 static int tofix=0;
361
362 static void ResetRL(uint8 *target)
363 {
364  memset(target,0xFF,256);
365  if(InputScanlineHook)
366   InputScanlineHook(0,0,0,0);
367  Plinef=target;
368  Pline=target;
369  firsttile=0;
370  linestartts=timestamp*48+X.count;
371  tofix=0;
372  FCEUPPU_LineUpdate098();
373  tofix=1;
374 }
375
376 extern uint8 sprlinebuf[256+8];
377
378 void FCEUPPU_LineUpdate098(void)
379 {
380  if(Pline)
381  {
382   int l=GETLASTPIXEL;
383   RefreshLine098(l);
384  }
385 }
386
387 static int rendis = 0;
388
389 void FCEUI_SetRenderDisable(int sprites, int bg)
390 {
391  //printf("%d, %d\n",sprites,bg);
392  if(sprites >= 0)
393  {
394   if(sprites == 2) rendis ^= 1;
395   else rendis = (rendis &~1) | sprites?1:0;
396  }
397  if(bg >= 0)
398  {
399   if(bg == 2) rendis ^= 2;
400   else rendis = (rendis &~2) | bg?2:0;
401  }
402 }
403
404 static void CheckSpriteHit(int p);
405
406 static void EndRL(void)
407 {
408  RefreshLine098(272);
409  if(tofix)
410   Fixit1();
411  CheckSpriteHit(272);
412  Pline=0;
413 }
414
415 static int32 sphitx;
416 static uint8 sphitdata;
417
418 static void CheckSpriteHit(int p)
419 {
420  int l=p-16;
421  int x;
422
423  if(sphitx==0x100) return;
424
425  for(x=sphitx;x<(sphitx+8) && x<l;x++)
426  {
427    if((sphitdata&(0x80>>(x-sphitx))) && !(Plinef[x]&64))
428    {
429     PPU_status|=0x40;
430     //printf("Ha:  %d, %d, Hita: %d, %d, %d, %d, %d\n",p,p&~7,scanline,GETLASTPIXEL-16,&Plinef[x],Pline,Pline-Plinef);
431     //printf("%d\n",GETLASTPIXEL-16);
432     //if(Plinef[x] == 0xFF)
433     //printf("PL: %d, %02x\n",scanline, Plinef[x]);
434     sphitx=0x100;
435     break;
436    }
437    }
438 }
439 static int spork=0;     /* spork the world.  Any sprites on this line?
440                            Then this will be set to 1.  Needed for zapper
441                            emulation and *gasp* sprite emulation.
442                         */
443
444 // lasttile is really "second to last tile."
445 static void FASTAPASS(1) RefreshLine098(int lastpixel)
446 {
447         static uint32 pshift[2];
448         static uint32 atlatch;
449         uint32 smorkus=RefreshAddr;
450
451         #define RefreshAddr smorkus
452         uint32 vofs;
453         int X1;
454
455         register uint8 *P=Pline;
456         int lasttile=lastpixel>>3;
457         int numtiles;
458         static int norecurse=0; /* Yeah, recursion would be bad.
459                                     PPU_hook() functions can call
460                                     mirroring/chr bank switching functions,
461                                     which call FCEUPPU_LineUpdate, which call this
462                                     function. */
463         if(norecurse) return;
464
465         if(sphitx != 0x100 && !(PPU_status&0x40))
466         {
467          if((sphitx < (lastpixel-16)) && !(sphitx < ((lasttile - 2)*8)))
468          {
469           //printf("OK: %d\n",scanline);
470           lasttile++;
471          }
472
473         }
474
475         if(lasttile>34) lasttile=34;
476         numtiles=lasttile-firsttile;
477
478         if(numtiles<=0) return;
479
480         P=Pline;
481
482         vofs=0;
483
484         vofs=((PPU[0]&0x10)<<8) | ((RefreshAddr>>12)&7);
485
486         if(!ScreenON && !SpriteON)
487         {
488          uint32 tem;
489          tem=Pal[0]|(Pal[0]<<8)|(Pal[0]<<16)|(Pal[0]<<24);
490          tem|=0x40404040;
491          FCEU_dwmemset(Pline,tem,numtiles*8);
492          P+=numtiles*8;
493          Pline=P;
494
495          firsttile=lasttile;
496
497          #define TOFIXNUM (272-0x4)
498          if(lastpixel>=TOFIXNUM && tofix)
499          {
500           Fixit1();
501           tofix=0;
502          }
503
504          if(InputScanlineHook && (lastpixel-16)>=0)
505          {
506           InputScanlineHook(Plinef,spork?sprlinebuf:0,linestartts,lasttile*8-16);
507          }
508          return;
509         }
510
511         /* Priority bits, needed for sprite emulation. */
512         Pal[0]|=64;
513         Pal[4]|=64;
514         Pal[8]|=64;
515         Pal[0xC]|=64;
516
517         /* This high-level graphics MMC5 emulation code was written
518            for MMC5 carts in "CL" mode.  It's probably not totally
519            correct for carts in "SL" mode.
520         */
521
522         #define PPUT_MMC5
523         if(MMC5Hack && geniestage!=1)
524         {
525          if(MMC5HackCHRMode==0 && (MMC5HackSPMode&0x80))
526          {
527           int tochange=MMC5HackSPMode&0x1F;
528           tochange-=firsttile;
529           for(X1=firsttile;X1<lasttile;X1++)
530           {
531            if((tochange<=0 && MMC5HackSPMode&0x40) || (tochange>0 && !(MMC5HackSPMode&0x40)))
532            {
533             #define PPUT_MMC5SP
534             #include "pputile098.h"
535             #undef PPUT_MMC5SP
536            }
537            else
538            {
539             #include "pputile098.h"
540            }
541            tochange--;
542           }
543          }
544          else if(MMC5HackCHRMode==1 && (MMC5HackSPMode&0x80))
545          {
546           int tochange=MMC5HackSPMode&0x1F;
547           tochange-=firsttile;
548
549           #define PPUT_MMC5SP
550           #define PPUT_MMC5CHR1
551           for(X1=firsttile;X1<lasttile;X1++)
552           {
553            #include "pputile098.h"
554           }
555           #undef PPUT_MMC5CHR1
556           #undef PPUT_MMC5SP
557          }
558          else if(MMC5HackCHRMode==1)
559          {
560           #define PPUT_MMC5CHR1
561           for(X1=firsttile;X1<lasttile;X1++)
562           {
563            #include "pputile098.h"
564           }
565           #undef PPUT_MMC5CHR1
566          }
567          else
568          {
569           for(X1=firsttile;X1<lasttile;X1++)
570           {
571            #include "pputile098.h"
572           }
573          }
574         }
575         #undef PPUT_MMC5
576         else if(PPU_hook)
577         {
578          norecurse=1;
579          #define PPUT_HOOK
580          for(X1=firsttile;X1<lasttile;X1++)
581          {
582           #include "pputile098.h"
583          }
584          #undef PPUT_HOOK
585          norecurse=0;
586         }
587         else
588         {
589          for(X1=firsttile;X1<lasttile;X1++)
590          {
591           #include "pputile098.h"
592          }
593         }
594
595         #undef vofs
596         #undef RefreshAddr
597
598         /* Reverse changes made before. */
599         Pal[0]&=63;
600         Pal[4]&=63;
601         Pal[8]&=63;
602         Pal[0xC]&=63;
603
604         RefreshAddr=smorkus;
605         if(firsttile<=2 && 2<lasttile && !(PPU[1]&2))
606         {
607          uint32 tem;
608          tem=Pal[0]|(Pal[0]<<8)|(Pal[0]<<16)|(Pal[0]<<24);
609          tem|=0x40404040;
610          *(uint32 *)Plinef=*(uint32 *)(Plinef+4)=tem;
611         }
612
613         if(!ScreenON)
614         {
615          uint32 tem;
616          int tstart,tcount;
617          tem=Pal[0]|(Pal[0]<<8)|(Pal[0]<<16)|(Pal[0]<<24);
618          tem|=0x40404040;
619
620          tcount=lasttile-firsttile;
621          tstart=firsttile-2;
622          if(tstart<0)
623          {
624           tcount+=tstart;
625           tstart=0;
626          }
627          if(tcount>0)
628           FCEU_dwmemset(Plinef+tstart*8,tem,tcount*8);
629         }
630
631         if(lastpixel>=TOFIXNUM && tofix)
632         {
633           //puts("Fixed");
634           Fixit1();
635           tofix=0;
636         }
637
638         //CheckSpriteHit(lasttile*8); //lasttile*8); //lastpixel);
639
640   CheckSpriteHit(lastpixel);  /* This only works right because
641                                            of a hack earlier in this function.
642                                         */
643         if(InputScanlineHook && (lastpixel-16)>=0)
644         {
645          InputScanlineHook(Plinef,spork?sprlinebuf:0,linestartts,lasttile*8-16);
646         }
647         Pline=P;
648         firsttile=lasttile;
649 }
650
651 static INLINE void Fixit2(void)
652 {
653    if(ScreenON || SpriteON)
654    {
655     uint32 rad=RefreshAddr;
656     rad&=0xFBE0;
657     rad|=TempAddr&0x041f;
658     RefreshAddr=rad;
659     //PPU_hook(RefreshAddr);
660     //PPU_hook(RefreshAddr,-1);
661    }
662 }
663
664 static void Fixit1(void)
665 {
666    if(ScreenON || SpriteON)
667    {
668     uint32 rad=RefreshAddr;
669
670     if((rad&0x7000)==0x7000)
671     {
672      rad^=0x7000;
673      if((rad&0x3E0)==0x3A0)
674      {
675       rad^=0x3A0;
676       rad^=0x800;
677      }
678      else
679      {
680       if((rad&0x3E0)==0x3e0)
681        rad^=0x3e0;
682       else rad+=0x20;
683      }
684     }
685     else
686      rad+=0x1000;
687     RefreshAddr=rad;
688     //PPU_hook(RefreshAddr); //,-1);
689    }
690 }
691
692 void MMC5_hb(int);     /* Ugh ugh ugh. */
693 static void DoLine(void)
694 {
695  int x;
696  uint8 *target=XBuf+scanline*320+32;
697
698  if(MMC5Hack && (ScreenON || SpriteON)) MMC5_hb(scanline);
699
700  X6502_Run(256);
701  EndRL();
702
703  if(rendis & 2)  /* User asked to not display background data. */
704  {
705   uint32 tem;
706   tem=Pal[0]|(Pal[0]<<8)|(Pal[0]<<16)|(Pal[0]<<24);
707   tem|=0x40404040;
708   FCEU_dwmemset(target,tem,256);
709  }
710
711  if(SpriteON)
712   CopySprites098(target);
713
714  if(ScreenON || SpriteON)  // Yes, very el-cheapo.
715  {
716   if(PPU[1]&0x01)
717   {
718    for(x=63;x>=0;x--)
719    *(uint32 *)&target[x<<2]=(*(uint32*)&target[x<<2])&0x30303030;
720   }
721  }
722  if((PPU[1]>>5)==0x7)
723  {
724   for(x=63;x>=0;x--)
725    *(uint32 *)&target[x<<2]=((*(uint32*)&target[x<<2])&0x3f3f3f3f)|0xc0c0c0c0;
726  }
727  else if(PPU[1]&0xE0)
728   for(x=63;x>=0;x--)
729    *(uint32 *)&target[x<<2]=(*(uint32*)&target[x<<2])|0x40404040;
730  else
731   for(x=63;x>=0;x--)
732    *(uint32 *)&target[x<<2]=((*(uint32*)&target[x<<2])&0x3f3f3f3f)|0x80808080;
733
734  sphitx=0x100;
735
736  if(ScreenON || SpriteON)
737   FetchSpriteData098();
738
739  if(GameHBIRQHook && (ScreenON || SpriteON) && ((PPU[0]&0x38)!=0x18))
740  {
741   X6502_Run(6);
742   Fixit2();
743   X6502_Run(4);
744   GameHBIRQHook();
745   X6502_Run(85-16-10);
746  }
747  else
748  {
749   X6502_Run(6);  // Tried 65, caused problems with Slalom(maybe others)
750   Fixit2();
751   X6502_Run(85-6-16);
752
753   // A semi-hack for Star Trek: 25th Anniversary
754   if(GameHBIRQHook && (ScreenON || SpriteON) && ((PPU[0]&0x38)!=0x18))
755    GameHBIRQHook();
756  }
757
758  if(SpriteON)
759   RefreshSprites098();
760  if(GameHBIRQHook2 && (ScreenON || SpriteON))
761   GameHBIRQHook2();
762  scanline++;
763  if(scanline<240)
764  {
765   ResetRL(XBuf+scanline*320+32);
766  }
767  X6502_Run(16);
768 }
769
770 #define V_FLIP  0x80
771 #define H_FLIP  0x40
772 #define SP_BACK 0x20
773
774 typedef struct {
775         uint8 y,no,atr,x;
776 } SPR;
777
778 typedef struct {
779         uint8 ca[2],atr,x;
780 } SPRB;
781
782 static uint8 numsprites,SpriteBlurp;
783 static void FetchSpriteData098(void)
784 {
785         uint8 ns,sb;
786         SPR *spr;
787         uint8 H;
788         int n;
789         int vofs;
790         uint8 P0=PPU[0];
791
792         spr=(SPR *)SPRAM;
793         H=8;
794
795         ns=sb=0;
796
797         vofs=(unsigned int)(P0&0x8&(((P0&0x20)^0x20)>>2))<<9;
798         H+=(P0&0x20)>>2;
799
800         if(!PPU_hook)
801          for(n=63;n>=0;n--,spr++)
802          {
803                 if((unsigned int)(scanline-spr->y)>=H) continue;
804                 //printf("%d, %u\n",scanline,(unsigned int)(scanline-spr->y));
805                 if(ns<maxsprites)
806                 {
807                  if(n==63) sb=1;
808
809                  {
810                   SPRB dst;
811                   uint8 *C;
812                   int t;
813                   unsigned int vadr;
814
815                   t = (int)scanline-(spr->y);
816
817       if(Sprite16)
818        vadr = ((spr->no&1)<<12) + ((spr->no&0xFE)<<4);
819       else
820        vadr = (spr->no<<4)+vofs;
821
822       if(spr->atr&V_FLIP)
823                   {
824                         vadr+=7;
825                         vadr-=t;
826                         vadr+=(P0&0x20)>>1;
827                         vadr-=t&8;
828                   }
829                   else
830                   {
831                         vadr+=t;
832                         vadr+=t&8;
833                   }
834
835                   /* Fix this geniestage hack */
836                   if(MMC5Hack && geniestage!=1) C = MMC5SPRVRAMADR(vadr);
837                   else C = VRAMADR(vadr);
838
839
840                   dst.ca[0]=C[0];
841                   dst.ca[1]=C[8];
842                   dst.x=spr->x;
843                   dst.atr=spr->atr;
844
845                   *(uint32 *)&SPRBUF[ns<<2]=*(uint32 *)&dst;
846                  }
847
848                  ns++;
849                 }
850                 else
851                 {
852                   PPU_status|=0x20;
853                   break;
854                 }
855          }
856         else
857          for(n=63;n>=0;n--,spr++)
858          {
859                 if((unsigned int)(scanline-spr->y)>=H) continue;
860
861                 if(ns<maxsprites)
862                 {
863                  if(n==63) sb=1;
864
865                  {
866                   SPRB dst;
867                   uint8 *C;
868                   int t;
869                   unsigned int vadr;
870
871                   t = (int)scanline-(spr->y);
872
873       if(Sprite16)
874        vadr = ((spr->no&1)<<12) + ((spr->no&0xFE)<<4);
875       else
876        vadr = (spr->no<<4)+vofs;
877
878       if(spr->atr&V_FLIP)
879                   {
880                         vadr+=7;
881                         vadr-=t;
882                         vadr+=(P0&0x20)>>1;
883                         vadr-=t&8;
884                   }
885                   else
886                   {
887                         vadr+=t;
888                         vadr+=t&8;
889                   }
890
891                   if(MMC5Hack) C = MMC5SPRVRAMADR(vadr);
892                   else C = VRAMADR(vadr);
893                   dst.ca[0]=C[0];
894                   if(ns<8)
895                   {
896                    PPU_hook(0x2000);
897                    PPU_hook(vadr);
898                   }
899                   dst.ca[1]=C[8];
900                   dst.x=spr->x;
901                   dst.atr=spr->atr;
902
903
904                   *(uint32 *)&SPRBUF[ns<<2]=*(uint32 *)&dst;
905                  }
906
907                  ns++;
908                 }
909                 else
910                 {
911                   PPU_status|=0x20;
912                   break;
913                 }
914          }
915         //if(ns>=7)
916         //printf("%d %d\n",scanline,ns);
917   if(ns>8) PPU_status|=0x20;  /* Handle case when >8 sprites per
918                                            scanline option is enabled. */
919         else if(PPU_hook)
920         {
921          for(n=0;n<(8-ns);n++)
922          {
923                  PPU_hook(0x2000);
924                  PPU_hook(vofs);
925          }
926         }
927         numsprites=ns;
928         SpriteBlurp=sb;
929 }
930
931 static void RefreshSprites098(void)
932 {
933         int n;
934         SPRB *spr;
935
936         spork=0;
937         if(!numsprites) return;
938
939         FCEU_dwmemset(sprlinebuf,0x80808080,256);
940         numsprites--;
941         spr = (SPRB*)SPRBUF+numsprites;
942
943        for(n=numsprites;n>=0;n--,spr--)
944        {
945          //#ifdef C80x86
946          //register uint32 pixdata asm ("eax");
947          //register uint8 J, atr;
948          //#else
949         register uint32 pixdata;
950         register uint8 J,atr;
951          //#endif
952
953         int x=spr->x;
954         uint8 *C;
955         uint8 *VB;
956
957         pixdata=ppulut1[spr->ca[0]]|ppulut2[spr->ca[1]];
958         J=spr->ca[0]|spr->ca[1];
959         atr=spr->atr;
960
961                        if(J)
962                        {
963                         if(n==0 && SpriteBlurp && !(PPU_status&0x40))
964                         {
965                          sphitx=x;
966                          sphitdata=J;
967                          if(atr&H_FLIP)
968                           sphitdata=    ((J<<7)&0x80) |
969                                         ((J<<5)&0x40) |
970                                         ((J<<3)&0x20) |
971                                         ((J<<1)&0x10) |
972                                         ((J>>1)&0x08) |
973                                         ((J>>3)&0x04) |
974                                         ((J>>5)&0x02) |
975                                         ((J>>7)&0x01);
976                         }
977
978          C = sprlinebuf+x;
979          VB = (PALRAM+0x10)+((atr&3)<<2);
980
981          if(atr&SP_BACK)
982          {
983     if(atr&H_FLIP)
984           {
985            if(J&0x80) C[7]=VB[pixdata&3]|0x40;
986            pixdata>>=4;
987            if(J&0x40) C[6]=VB[pixdata&3]|0x40;
988            pixdata>>=4;
989            if(J&0x20) C[5]=VB[pixdata&3]|0x40;
990            pixdata>>=4;
991            if(J&0x10) C[4]=VB[pixdata&3]|0x40;
992            pixdata>>=4;
993            if(J&0x08) C[3]=VB[pixdata&3]|0x40;
994            pixdata>>=4;
995            if(J&0x04) C[2]=VB[pixdata&3]|0x40;
996            pixdata>>=4;
997            if(J&0x02) C[1]=VB[pixdata&3]|0x40;
998            pixdata>>=4;
999            if(J&0x01) C[0]=VB[pixdata]|0x40;
1000           } else  {
1001            if(J&0x80) C[0]=VB[pixdata&3]|0x40;
1002            pixdata>>=4;
1003            if(J&0x40) C[1]=VB[pixdata&3]|0x40;
1004            pixdata>>=4;
1005            if(J&0x20) C[2]=VB[pixdata&3]|0x40;
1006            pixdata>>=4;
1007            if(J&0x10) C[3]=VB[pixdata&3]|0x40;
1008            pixdata>>=4;
1009            if(J&0x08) C[4]=VB[pixdata&3]|0x40;
1010            pixdata>>=4;
1011            if(J&0x04) C[5]=VB[pixdata&3]|0x40;
1012            pixdata>>=4;
1013            if(J&0x02) C[6]=VB[pixdata&3]|0x40;
1014            pixdata>>=4;
1015            if(J&0x01) C[7]=VB[pixdata]|0x40;
1016           }
1017          } else {
1018     if(atr&H_FLIP)
1019           {
1020            if(J&0x80) C[7]=VB[pixdata&3];
1021            pixdata>>=4;
1022            if(J&0x40) C[6]=VB[pixdata&3];
1023            pixdata>>=4;
1024            if(J&0x20) C[5]=VB[pixdata&3];
1025            pixdata>>=4;
1026            if(J&0x10) C[4]=VB[pixdata&3];
1027            pixdata>>=4;
1028            if(J&0x08) C[3]=VB[pixdata&3];
1029            pixdata>>=4;
1030            if(J&0x04) C[2]=VB[pixdata&3];
1031            pixdata>>=4;
1032            if(J&0x02) C[1]=VB[pixdata&3];
1033            pixdata>>=4;
1034            if(J&0x01) C[0]=VB[pixdata];
1035           }else{
1036            if(J&0x80) C[0]=VB[pixdata&3];
1037            pixdata>>=4;
1038            if(J&0x40) C[1]=VB[pixdata&3];
1039            pixdata>>=4;
1040            if(J&0x20) C[2]=VB[pixdata&3];
1041            pixdata>>=4;
1042            if(J&0x10) C[3]=VB[pixdata&3];
1043            pixdata>>=4;
1044            if(J&0x08) C[4]=VB[pixdata&3];
1045            pixdata>>=4;
1046            if(J&0x04) C[5]=VB[pixdata&3];
1047            pixdata>>=4;
1048            if(J&0x02) C[6]=VB[pixdata&3];
1049            pixdata>>=4;
1050            if(J&0x01) C[7]=VB[pixdata];
1051           }
1052          }
1053         }
1054       }
1055      SpriteBlurp=0;
1056         spork=1;
1057 }
1058
1059 static void CopySprites098(uint8 *target)
1060 {
1061       uint8 n=((PPU[1]&4)^4)<<1;
1062       uint8 *P=target;
1063
1064       if(!spork) return;
1065       spork=0;
1066
1067       if(rendis & 1) return;  /* User asked to not display sprites. */
1068
1069       loopskie:
1070       {
1071        uint32 t=*(uint32 *)(sprlinebuf+n);
1072
1073        if(t!=0x80808080)
1074        {
1075         #ifdef LSB_FIRST
1076         if(!(t&0x80))
1077         {
1078          if(!(t&0x40) || (P[n]&0x40))       // Normal sprite || behind bg sprite
1079           P[n]=sprlinebuf[n];
1080         }
1081
1082         if(!(t&0x8000))
1083         {
1084          if(!(t&0x4000) || (P[n+1]&0x40))       // Normal sprite || behind bg sprite
1085           P[n+1]=(sprlinebuf+1)[n];
1086         }
1087
1088         if(!(t&0x800000))
1089         {
1090          if(!(t&0x400000) || (P[n+2]&0x40))       // Normal sprite || behind bg sprite
1091           P[n+2]=(sprlinebuf+2)[n];
1092         }
1093
1094         if(!(t&0x80000000))
1095         {
1096          if(!(t&0x40000000) || (P[n+3]&0x40))       // Normal sprite || behind bg sprite
1097           P[n+3]=(sprlinebuf+3)[n];
1098         }
1099         #else
1100         /* TODO:  Simplify */
1101         if(!(t&0x80000000))
1102         {
1103          if(!(t&0x40000000))       // Normal sprite
1104           P[n]=sprlinebuf[n];
1105    else if(P[n]&64)  // behind bg sprite
1106           P[n]=sprlinebuf[n];
1107         }
1108
1109         if(!(t&0x800000))
1110         {
1111          if(!(t&0x400000))       // Normal sprite
1112           P[n+1]=(sprlinebuf+1)[n];
1113    else if(P[n+1]&64)  // behind bg sprite
1114           P[n+1]=(sprlinebuf+1)[n];
1115         }
1116
1117         if(!(t&0x8000))
1118         {
1119          if(!(t&0x4000))       // Normal sprite
1120           P[n+2]=(sprlinebuf+2)[n];
1121    else if(P[n+2]&64)  // behind bg sprite
1122           P[n+2]=(sprlinebuf+2)[n];
1123         }
1124
1125         if(!(t&0x80))
1126         {
1127          if(!(t&0x40))       // Normal sprite
1128           P[n+3]=(sprlinebuf+3)[n];
1129    else if(P[n+3]&64)  // behind bg sprite
1130           P[n+3]=(sprlinebuf+3)[n];
1131         }
1132         #endif
1133        }
1134       }
1135       n+=4;
1136       if(n) goto loopskie;
1137 }
1138
1139 void FCEUPPU_Init(void)
1140 {
1141  makeppulut();
1142 }
1143
1144 void FCEUPPU_Reset(void)
1145 {
1146         VRAMBuffer=PPU[0]=PPU[1]=PPU_status=PPU[3]=0;
1147         PPUSPL=0;
1148         PPUGenLatch=0;
1149         RefreshAddr=TempAddr=0;
1150         vtoggle = 0;
1151         ppudead = 2;
1152         kook = 0;
1153
1154 //      XOffset=0;
1155 }
1156
1157 void FCEUPPU_Power(void)
1158 {
1159         int x;
1160
1161         memset(NTARAM,0x00,0x800);
1162         memset(PALRAM,0x00,0x20);
1163         memset(SPRAM,0x00,0x100);
1164         FCEUPPU_Reset();
1165
1166         for(x=0x2000;x<0x4000;x+=8)
1167         {
1168          ARead[x]=A200x;
1169          BWrite[x]=B2000;
1170          ARead[x+1]=A200x;
1171          BWrite[x+1]=B2001;
1172          ARead[x+2]=A2002;
1173          BWrite[x+2]=B2002;
1174          ARead[x+3]=A200x;
1175          BWrite[x+3]=B2003;
1176          ARead[x+4]=A200x; //A2004;
1177          BWrite[x+4]=B2004;
1178          ARead[x+5]=A200x;
1179          BWrite[x+5]=B2005;
1180          ARead[x+6]=A200x;
1181          BWrite[x+6]=B2006;
1182          ARead[x+7]=A2007;
1183          BWrite[x+7]=B2007;
1184         }
1185         BWrite[0x4014]=B4014;
1186 }
1187
1188
1189 void FCEUPPU_Loop(int skip)
1190 {
1191   uint32 scanlines_per_frame = PAL ? 312 : 262;
1192
1193   if(ppudead) /* Needed for Knight Rider, possibly others. */
1194   {
1195    //memset(XBuf, 0x80, 256*240);
1196    X6502_Run(scanlines_per_frame*(256+85));
1197    ppudead--;
1198   }
1199   else
1200   {
1201    X6502_Run(256+85);
1202    PPU_status |= 0x80;
1203    PPU[3]=PPUSPL=0;       /* Not sure if this is correct.  According
1204                                   to Matt Conte and my own tests, it is.  Timing is probably
1205                                   off, though.  NOTE:  Not having this here
1206                                   breaks a Super Donkey Kong game. */
1207                                 /* I need to figure out the true nature and length
1208                                    of this delay.
1209                                 */
1210    X6502_Run(12);
1211    if(FCEUGameInfo.type==GIT_NSF)
1212     DoNSFFrame();
1213    else
1214    {
1215     if(VBlankON)
1216      TriggerNMI();
1217    }
1218    X6502_Run((scanlines_per_frame-242)*(256+85)-12); //-12);
1219    PPU_status&=0x1f;
1220    X6502_Run(256);
1221
1222    {
1223     int x;
1224
1225     if(ScreenON || SpriteON)
1226     {
1227      if(GameHBIRQHook && ((PPU[0]&0x38)!=0x18))
1228       GameHBIRQHook();
1229      if(PPU_hook)
1230       for(x=0;x<42;x++) {PPU_hook(0x2000); PPU_hook(0);}
1231      if(GameHBIRQHook2)
1232       GameHBIRQHook2();
1233     }
1234     X6502_Run(85-16);
1235     if(ScreenON || SpriteON)
1236     {
1237      RefreshAddr=TempAddr;
1238      if(PPU_hook) PPU_hook(RefreshAddr&0x3fff);
1239     }
1240
1241     /* Clean this stuff up later. */
1242     spork=numsprites=0;
1243     ResetRL(XBuf+32);
1244
1245     X6502_Run(16-kook);
1246     kook ^= 1;
1247    }
1248    if(FCEUGameInfo.type==GIT_NSF)
1249    {
1250     X6502_Run((256+85)*240);
1251    }
1252    #ifdef FRAMESKIP
1253    else if(skip)
1254    {
1255     int y;
1256
1257     y=SPRAM[0];
1258     y++;
1259
1260     PPU_status|=0x20;       // Fixes "Bee 52".  Does it break anything?
1261     if(GameHBIRQHook)
1262     {
1263      X6502_Run(256);
1264      for(scanline=0;scanline<240;scanline++)
1265      {
1266       if(ScreenON || SpriteON)
1267        GameHBIRQHook();
1268       if(scanline==y && SpriteON) PPU_status|=0x40;
1269       X6502_Run((scanline==239)?85:(256+85));
1270      }
1271     }
1272     else if(y<240)
1273     {
1274      X6502_Run((256+85)*y);
1275      if(SpriteON) PPU_status|=0x40; // Quick and very dirty hack.
1276      X6502_Run((256+85)*(240-y));
1277     }
1278     else
1279      X6502_Run((256+85)*240);
1280    }
1281    #endif
1282    else
1283    {
1284     int x,max,maxref;
1285
1286     deemp=PPU[1]>>5;
1287     for(scanline=0;scanline<240;)       //scanline is incremented in  DoLine.  Evil. :/
1288     {
1289      deempcnt[deemp]++;
1290 #ifdef WIN32
1291      if((PPUViewer) && (scanline == PPUViewScanline)) UpdatePPUView(1);
1292 #endif
1293      DoLine();
1294     }
1295     if(MMC5Hack && (ScreenON || SpriteON)) MMC5_hb(scanline);
1296     for(x=1,max=0,maxref=0;x<7;x++)
1297     {
1298      if(deempcnt[x]>max)
1299      {
1300       max=deempcnt[x];
1301       maxref=x;
1302      }
1303      deempcnt[x]=0;
1304     }
1305     //FCEU_DispMessage("%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x %d",deempcnt[0],deempcnt[1],deempcnt[2],deempcnt[3],deempcnt[4],deempcnt[5],deempcnt[6],deempcnt[7],maxref);
1306     //memset(deempcnt,0,sizeof(deempcnt));
1307     SetNESDeemph(maxref,0);
1308    }
1309   } /* else... to if(ppudead) */
1310 }
1311
1312