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