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