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