release r1
[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
cd9a6b51 89#if defined(ASM_6502) && !defined(DEBUG_ASM_6502)
77887306 90static 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}
6244011f 115#endif
116
117extern int ppudead;
118extern int kook;
119
120/* Color deemphasis emulation. Joy... */
121static uint8 deemp=0;
122static int deempcnt[8];
123
124extern int maxsprites;
125
126extern uint8 PPUSPL;
127extern uint8 SPRAM[0x100];
128extern 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
136static 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
152static DECLFR(A200x) /* Not correct for $2004 reads. */
153{
154 FCEUPPU_LineUpdate098();
155 return PPUGenLatch;
156}
157
158/*
159static 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*/
183static 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
212static 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();
77887306 222 TriggerNMI(); // TODO?
6244011f 223 }
224 PPU[0]=V;
225 TempAddr&=0xF3FF;
226 TempAddr|=(V&3)<<10;
227}
228
229static 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
239static DECLFW(B2002)
240{
241 PPUGenLatch=V;
242}
243
244static 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
252static 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
272static 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
294static 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
317static 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
344static 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
360static uint8 *Pline=0,*Plinef;
361static int firsttile;
362static int linestartts;
363static int tofix=0;
364
365static void ResetRL(uint8 *target)
366{
77887306 367 FCEU_dwmemset(target,0xffffffff,256);
6244011f 368 if(InputScanlineHook)
369 InputScanlineHook(0,0,0,0);
370 Plinef=target;
371 Pline=target;
372 firsttile=0;
642070a9 373 linestartts=timestamp*48+X6502_GetCycleCount();
6244011f 374 tofix=0;
375 FCEUPPU_LineUpdate098();
376 tofix=1;
377}
378
379extern uint8 sprlinebuf[256+8];
380
381void FCEUPPU_LineUpdate098(void)
382{
383 if(Pline)
384 {
385 int l=GETLASTPIXEL;
386 RefreshLine098(l);
387 }
388}
389
390static int rendis = 0;
391
392void 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
407static void CheckSpriteHit(int p);
408
409static void EndRL(void)
410{
411 RefreshLine098(272);
412 if(tofix)
413 Fixit1();
414 CheckSpriteHit(272);
415 Pline=0;
416}
417
418static int32 sphitx;
419static uint8 sphitdata;
420
421static 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}
442static 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."
448static 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;
642070a9 492 int tiles;
6244011f 493 tem=Pal[0]|(Pal[0]<<8)|(Pal[0]<<16)|(Pal[0]<<24);
494 tem|=0x40404040;
642070a9 495 tiles=numtiles;
496 if(firsttile+tiles > 256/8) tiles=256/8-firsttile;
497 if(tiles > 0)
498 FCEU_dwmemset(Pline,tem,tiles*8);
6244011f 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
658static 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
671static 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
699void MMC5_hb(int); /* Ugh ugh ugh. */
700static void DoLine(void)
701{
c8c88d89 702#ifndef __arm__
6244011f 703 int x;
cd9a6b51 704#endif
6244011f 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
c8c88d89 723#ifdef __arm__
77887306 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
6244011f 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;
77887306 755#endif
6244011f 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
797typedef struct {
798 uint8 y,no,atr,x;
799} SPR;
800
801typedef struct {
802 uint8 ca[2],atr,x;
803} SPRB;
804
805static uint8 numsprites,SpriteBlurp;
806static 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
954static 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
1082static 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
1162void FCEUPPU_Init(void)
1163{
1164 makeppulut();
1165}
1166
1167void 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
1180void 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
1212void 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);
a384bf44 1219 //X6502_Run(scanlines_per_frame*(256+85));
1220 int lines;
1221 for (lines=scanlines_per_frame;lines;lines--)
1222 X6502_Run(256+85);
6244011f 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 }
a384bf44 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 }
6244011f 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 {
a384bf44 1282 // run scanlines for asm core to fuction
1283 for(scanline=0;scanline<240;scanline++)
1284 X6502_Run(256+85);
6244011f 1285 }
1286 #ifdef FRAMESKIP
1287 else if(skip)
1288 {
a384bf44 1289 int y, lines;
6244011f 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 {
a384bf44 1308 for (lines=y;lines;lines--)
1309 X6502_Run(256+85);
6244011f 1310 if(SpriteON) PPU_status|=0x40; // Quick and very dirty hack.
a384bf44 1311 for (lines=240-y;lines;lines--)
1312 X6502_Run(256+85);
6244011f 1313 }
1314 else
a384bf44 1315 {
1316 for (lines=240;lines;lines--)
1317 X6502_Run(256+85);
1318 }
6244011f 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