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