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