release r2, update credits
[fceu.git] / ppu098.c
CommitLineData
6244011f 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"
77887306 29#include "sound.h"
6244011f 30#include "memory.h"
31
32#include "cart.h"
33#include "palette.h"
34#include "video.h"
35#include "input.h"
36
c8c88d89 37#ifdef __arm__
38#include "drivers/arm/asmutils.h"
77887306 39#endif
40
6244011f 41#define Pal (PALRAM)
42
43static void FetchSpriteData098(void);
44static void FASTAPASS(1) RefreshLine098(int lastpixel);
45static void RefreshSprites098(void);
46static void CopySprites098(uint8 *target);
47
48static void Fixit1(void);
49static uint32 ppulut1[256];
50static uint32 ppulut2[256];
51static uint32 ppulut3[128];
52
53static 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
6244011f 89extern int ppudead;
90extern int kook;
91
92/* Color deemphasis emulation. Joy... */
93static uint8 deemp=0;
94static int deempcnt[8];
95
96extern int maxsprites;
97
98extern uint8 PPUSPL;
99extern uint8 SPRAM[0x100];
100extern 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
108static 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
124static DECLFR(A200x) /* Not correct for $2004 reads. */
125{
126 FCEUPPU_LineUpdate098();
127 return PPUGenLatch;
128}
129
130/*
131static 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*/
155static 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
184static 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();
77887306 194 TriggerNMI(); // TODO?
6244011f 195 }
196 PPU[0]=V;
197 TempAddr&=0xF3FF;
198 TempAddr|=(V&3)<<10;
199}
200
201static 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
211static DECLFW(B2002)
212{
213 PPUGenLatch=V;
214}
215
216static 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
224static 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
244static 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
266static 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
289static 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
316static 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
332static uint8 *Pline=0,*Plinef;
333static int firsttile;
334static int linestartts;
335static int tofix=0;
336
337static void ResetRL(uint8 *target)
338{
77887306 339 FCEU_dwmemset(target,0xffffffff,256);
6244011f 340 if(InputScanlineHook)
341 InputScanlineHook(0,0,0,0);
342 Plinef=target;
343 Pline=target;
344 firsttile=0;
642070a9 345 linestartts=timestamp*48+X6502_GetCycleCount();
6244011f 346 tofix=0;
347 FCEUPPU_LineUpdate098();
348 tofix=1;
349}
350
351extern uint8 sprlinebuf[256+8];
352
353void FCEUPPU_LineUpdate098(void)
354{
355 if(Pline)
356 {
357 int l=GETLASTPIXEL;
358 RefreshLine098(l);
359 }
360}
361
362static int rendis = 0;
363
364void 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
379static void CheckSpriteHit(int p);
380
381static void EndRL(void)
382{
383 RefreshLine098(272);
384 if(tofix)
385 Fixit1();
386 CheckSpriteHit(272);
387 Pline=0;
388}
389
390static int32 sphitx;
391static uint8 sphitdata;
392
393static 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}
414static 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."
420static 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;
642070a9 464 int tiles;
6244011f 465 tem=Pal[0]|(Pal[0]<<8)|(Pal[0]<<16)|(Pal[0]<<24);
466 tem|=0x40404040;
642070a9 467 tiles=numtiles;
468 if(firsttile+tiles > 256/8) tiles=256/8-firsttile;
469 if(tiles > 0)
470 FCEU_dwmemset(Pline,tem,tiles*8);
6244011f 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
630static 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
643static 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
671void MMC5_hb(int); /* Ugh ugh ugh. */
672static void DoLine(void)
673{
c8c88d89 674#ifndef __arm__
6244011f 675 int x;
cd9a6b51 676#endif
6244011f 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
c8c88d89 695#ifdef __arm__
77887306 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
6244011f 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;
77887306 727#endif
6244011f 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
769typedef struct {
770 uint8 y,no,atr,x;
771} SPR;
772
773typedef struct {
774 uint8 ca[2],atr,x;
775} SPRB;
776
777static uint8 numsprites,SpriteBlurp;
778static 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
926static 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
1054static 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
1134void FCEUPPU_Init(void)
1135{
1136 makeppulut();
1137}
1138
1139void 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
1152void 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
1184void 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);
a384bf44 1191 //X6502_Run(scanlines_per_frame*(256+85));
1192 int lines;
1193 for (lines=scanlines_per_frame;lines;lines--)
1194 X6502_Run(256+85);
6244011f 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 }
a384bf44 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 }
6244011f 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 {
a384bf44 1254 // run scanlines for asm core to fuction
1255 for(scanline=0;scanline<240;scanline++)
1256 X6502_Run(256+85);
6244011f 1257 }
1258 #ifdef FRAMESKIP
1259 else if(skip)
1260 {
a384bf44 1261 int y, lines;
6244011f 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 {
a384bf44 1280 for (lines=y;lines;lines--)
1281 X6502_Run(256+85);
6244011f 1282 if(SpriteON) PPU_status|=0x40; // Quick and very dirty hack.
a384bf44 1283 for (lines=240-y;lines;lines--)
1284 X6502_Run(256+85);
6244011f 1285 }
1286 else
a384bf44 1287 {
1288 for (lines=240;lines;lines--)
1289 X6502_Run(256+85);
1290 }
6244011f 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