1 /* FCE Ultra - NES/Famicom Emulator
3 * Copyright notice for this file:
4 * Copyright (C) 1998 BERO
5 * Copyright (C) 2003 Xodnizel
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.
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.
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
38 #include "drivers/gp2x/asmutils.h"
43 static void FetchSpriteData098(void);
44 static void FASTAPASS(1) RefreshLine098(int lastpixel);
45 static void RefreshSprites098(void);
46 static void CopySprites098(uint8 *target);
48 static void Fixit1(void);
49 static uint32 ppulut1[256];
50 static uint32 ppulut2[256];
51 static uint32 ppulut3[128];
53 static void makeppulut(void)
62 ppulut1[x]|=((x>>(7-y))&1)<<(y*4);
63 ppulut2[x]=ppulut1[x]<<1;
74 ppulut3[xo|(cc<<3)]=0;
75 for(pixel=0;pixel<8;pixel++)
80 ppulut3[xo|(cc<<3)]|=(( cc>>shiftr )&3)<<(2+pixel*4);
82 // printf("%08x\n",ppulut3[xo|(cc<<3)]);
89 #if defined(ASM_6502) && !defined(DEBUG_ASM_6502)
90 static void asmcpu_update(int32 cycles)
92 // some code from x6502.c
106 if((PSG[0x10]&0x80) && !(PSG[0x10]&0x40))
108 extern uint8 SIRQStat;
110 X6502_IRQBegin(FCEU_IQDPCM);
120 /* Color deemphasis emulation. Joy... */
121 static uint8 deemp=0;
122 static int deempcnt[8];
124 extern int maxsprites;
127 extern uint8 SPRAM[0x100];
128 extern uint8 SPRBUF[0x100];
131 #define MMC5SPRVRAMADR(V) &MMC5SPRVPage[(V)>>10][(V)]
132 #define MMC5BGVRAMADR(V) &MMC5BGVPage[(V)>>10][(V)]
133 #define VRAMADR(V) &VPage[(V)>>10][(V)]
140 FCEUPPU_LineUpdate098();
142 ret|=PPUGenLatch&0x1F;
152 static DECLFR(A200x) /* Not correct for $2004 reads. */
154 FCEUPPU_LineUpdate098();
163 FCEUPPU_LineUpdate098();
173 //printf("$%02x:$%02x\n",PPUSPL,V);
179 printf("%d, %02x\n",scanline,ret);
186 uint32 tmp=RefreshAddr&0x3FFF;
188 FCEUPPU_LineUpdate098();
193 if(PPU_hook) PPU_hook(tmp);
194 PPUGenLatch=VRAMBuffer;
197 VRAMBuffer=VPage[tmp>>10][tmp];
201 VRAMBuffer=vnapage[(tmp>>10)&0x3][tmp&0x3FF];
205 if(INC32) RefreshAddr+=32;
207 if(PPU_hook) PPU_hook(RefreshAddr&0x3fff);
214 // FCEU_printf("%04x:%02x, (%d) %02x, %02x\n",A,V,scanline,PPU[0],PPU_status);
216 FCEUPPU_LineUpdate098();
218 if(!(PPU[0]&0x80) && (V&0x80) && (PPU_status&0x80))
220 // FCEU_printf("Trigger NMI, %d, %d\n",timestamp,ppudead);
222 TriggerNMI(); // TODO?
231 //printf("%04x:$%02x, %d\n",A,V,scanline);
232 FCEUPPU_LineUpdate098();
246 //printf("$%04x:$%02x, %d, %d\n",A,V,timestamp,scanline);
254 //printf("Wr: %04x:$%02x\n",A,V);
264 //printf("$%02x:$%02x\n",PPUSPL,V);
275 FCEUPPU_LineUpdate098();
296 FCEUPPU_LineUpdate098();
302 TempAddr|=(V&0x3f)<<8;
309 RefreshAddr=TempAddr;
311 PPU_hook(RefreshAddr);
312 //printf("%d, %04x\n",scanline,RefreshAddr);
319 uint32 tmp=RefreshAddr&0x3FFF;
325 PALRAM[0x00]=PALRAM[0x04]=PALRAM[0x08]=PALRAM[0x0C]=V&0x3F;
326 else if(tmp&3) PALRAM[(tmp&0x1f)]=V&0x3f;
330 if(PPUCHRRAM&(1<<(tmp>>10)))
331 VPage[tmp>>10][tmp]=V;
335 if(PPUNTARAM&(1<<((tmp&0xF00)>>10)))
336 vnapage[((tmp&0xF00)>>10)][tmp&0x3FF]=V;
338 // FCEU_printf("ppu (%04x) %04x:%04x %d, %d\n",X.PC,RefreshAddr,PPUGenLatch,scanline,timestamp);
339 if(INC32) RefreshAddr+=32;
341 if(PPU_hook) PPU_hook(RefreshAddr&0x3fff);
350 // X6502_DMW(0x2004,X6502_DMR(t+x));
352 B2004(0x2004,X.DB=ARead[t+x](t+x));
353 X6502_AddCycles(512);
356 #define PAL(c) ((c)+cc)
358 #define GETLASTPIXEL (PAL?((timestamp*48-linestartts)/15) : ((timestamp*48-linestartts)>>4) )
360 static uint8 *Pline=0,*Plinef;
361 static int firsttile;
362 static int linestartts;
365 static void ResetRL(uint8 *target)
367 FCEU_dwmemset(target,0xffffffff,256);
368 if(InputScanlineHook)
369 InputScanlineHook(0,0,0,0);
373 linestartts=timestamp*48+X6502_GetCycleCount();
375 FCEUPPU_LineUpdate098();
379 extern uint8 sprlinebuf[256+8];
381 void FCEUPPU_LineUpdate098(void)
390 static int rendis = 0;
392 void FCEUI_SetRenderDisable(int sprites, int bg)
394 //printf("%d, %d\n",sprites,bg);
397 if(sprites == 2) rendis ^= 1;
398 else rendis = (rendis &~1) | sprites?1:0;
402 if(bg == 2) rendis ^= 2;
403 else rendis = (rendis &~2) | bg?2:0;
407 static void CheckSpriteHit(int p);
409 static void EndRL(void)
419 static uint8 sphitdata;
421 static void CheckSpriteHit(int p)
426 if(sphitx==0x100) return;
428 for(x=sphitx;x<(sphitx+8) && x<l;x++)
430 if((sphitdata&(0x80>>(x-sphitx))) && !(Plinef[x]&64))
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]);
442 static int spork=0; /* spork the world. Any sprites on this line?
443 Then this will be set to 1. Needed for zapper
444 emulation and *gasp* sprite emulation.
447 // lasttile is really "second to last tile."
448 static void FASTAPASS(1) RefreshLine098(int lastpixel)
450 static uint32 pshift[2];
451 static uint32 atlatch;
452 uint32 smorkus=RefreshAddr;
454 #define RefreshAddr smorkus
458 register uint8 *P=Pline;
459 int lasttile=lastpixel>>3;
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
466 if(norecurse) return;
468 if(sphitx != 0x100 && !(PPU_status&0x40))
470 if((sphitx < (lastpixel-16)) && !(sphitx < ((lasttile - 2)*8)))
472 //printf("OK: %d\n",scanline);
478 if(lasttile>34) lasttile=34;
479 numtiles=lasttile-firsttile;
481 if(numtiles<=0) return;
487 vofs=((PPU[0]&0x10)<<8) | ((RefreshAddr>>12)&7);
489 if(!ScreenON && !SpriteON)
493 tem=Pal[0]|(Pal[0]<<8)|(Pal[0]<<16)|(Pal[0]<<24);
496 if(firsttile+tiles > 256/8) tiles=256/8-firsttile;
498 FCEU_dwmemset(Pline,tem,tiles*8);
504 #define TOFIXNUM (272-0x4)
505 if(lastpixel>=TOFIXNUM && tofix)
511 if(InputScanlineHook && (lastpixel-16)>=0)
513 InputScanlineHook(Plinef,spork?sprlinebuf:0,linestartts,lasttile*8-16);
518 /* Priority bits, needed for sprite emulation. */
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.
530 if(MMC5Hack && geniestage!=1)
532 if(MMC5HackCHRMode==0 && (MMC5HackSPMode&0x80))
534 int tochange=MMC5HackSPMode&0x1F;
536 for(X1=firsttile;X1<lasttile;X1++)
538 if((tochange<=0 && MMC5HackSPMode&0x40) || (tochange>0 && !(MMC5HackSPMode&0x40)))
541 #include "pputile098.h"
546 #include "pputile098.h"
551 else if(MMC5HackCHRMode==1 && (MMC5HackSPMode&0x80))
553 int tochange=MMC5HackSPMode&0x1F;
557 #define PPUT_MMC5CHR1
558 for(X1=firsttile;X1<lasttile;X1++)
560 #include "pputile098.h"
565 else if(MMC5HackCHRMode==1)
567 #define PPUT_MMC5CHR1
568 for(X1=firsttile;X1<lasttile;X1++)
570 #include "pputile098.h"
576 for(X1=firsttile;X1<lasttile;X1++)
578 #include "pputile098.h"
587 for(X1=firsttile;X1<lasttile;X1++)
589 #include "pputile098.h"
596 for(X1=firsttile;X1<lasttile;X1++)
598 #include "pputile098.h"
605 /* Reverse changes made before. */
612 if(firsttile<=2 && 2<lasttile && !(PPU[1]&2))
615 tem=Pal[0]|(Pal[0]<<8)|(Pal[0]<<16)|(Pal[0]<<24);
617 *(uint32 *)Plinef=*(uint32 *)(Plinef+4)=tem;
624 tem=Pal[0]|(Pal[0]<<8)|(Pal[0]<<16)|(Pal[0]<<24);
627 tcount=lasttile-firsttile;
635 FCEU_dwmemset(Plinef+tstart*8,tem,tcount*8);
638 if(lastpixel>=TOFIXNUM && tofix)
645 //CheckSpriteHit(lasttile*8); //lasttile*8); //lastpixel);
647 CheckSpriteHit(lastpixel); /* This only works right because
648 of a hack earlier in this function.
650 if(InputScanlineHook && (lastpixel-16)>=0)
652 InputScanlineHook(Plinef,spork?sprlinebuf:0,linestartts,lasttile*8-16);
658 static INLINE void Fixit2(void)
660 if(ScreenON || SpriteON)
662 uint32 rad=RefreshAddr;
664 rad|=TempAddr&0x041f;
666 //PPU_hook(RefreshAddr);
667 //PPU_hook(RefreshAddr,-1);
671 static void Fixit1(void)
673 if(ScreenON || SpriteON)
675 uint32 rad=RefreshAddr;
677 if((rad&0x7000)==0x7000)
680 if((rad&0x3E0)==0x3A0)
687 if((rad&0x3E0)==0x3e0)
695 //PPU_hook(RefreshAddr); //,-1);
699 void MMC5_hb(int); /* Ugh ugh ugh. */
700 static void DoLine(void)
705 uint8 *target=XBuf+scanline*320+32;
707 if(MMC5Hack && (ScreenON || SpriteON)) MMC5_hb(scanline);
712 if(rendis & 2) /* User asked to not display background data. */
715 tem=Pal[0]|(Pal[0]<<8)|(Pal[0]<<16)|(Pal[0]<<24);
717 FCEU_dwmemset(target,tem,256);
721 CopySprites098(target);
724 if(ScreenON || SpriteON) // Yes, very el-cheapo.
727 block_and(target, 256, 0x30);
730 block_or(target, 256, 0xc0);
732 block_or(target, 256, 0x40);
734 block_andor(target, 256, 0x3f, 0x80);
736 if(ScreenON || SpriteON) // Yes, very el-cheapo.
741 *(uint32 *)&target[x<<2]=(*(uint32*)&target[x<<2])&0x30303030;
747 *(uint32 *)&target[x<<2]=((*(uint32*)&target[x<<2])&0x3f3f3f3f)|0xc0c0c0c0;
751 *(uint32 *)&target[x<<2]=(*(uint32*)&target[x<<2])|0x40404040;
754 *(uint32 *)&target[x<<2]=((*(uint32*)&target[x<<2])&0x3f3f3f3f)|0x80808080;
759 if(ScreenON || SpriteON)
760 FetchSpriteData098();
762 if(GameHBIRQHook && (ScreenON || SpriteON) && ((PPU[0]&0x38)!=0x18))
772 X6502_Run(6); // Tried 65, caused problems with Slalom(maybe others)
776 // A semi-hack for Star Trek: 25th Anniversary
777 if(GameHBIRQHook && (ScreenON || SpriteON) && ((PPU[0]&0x38)!=0x18))
783 if(GameHBIRQHook2 && (ScreenON || SpriteON))
788 ResetRL(XBuf+scanline*320+32);
805 static uint8 numsprites,SpriteBlurp;
806 static void FetchSpriteData098(void)
820 vofs=(unsigned int)(P0&0x8&(((P0&0x20)^0x20)>>2))<<9;
824 for(n=63;n>=0;n--,spr++)
826 if((unsigned int)(scanline-spr->y)>=H) continue;
827 //printf("%d, %u\n",scanline,(unsigned int)(scanline-spr->y));
838 t = (int)scanline-(spr->y);
841 vadr = ((spr->no&1)<<12) + ((spr->no&0xFE)<<4);
843 vadr = (spr->no<<4)+vofs;
858 /* Fix this geniestage hack */
859 if(MMC5Hack && geniestage!=1) C = MMC5SPRVRAMADR(vadr);
860 else C = VRAMADR(vadr);
868 *(uint32 *)&SPRBUF[ns<<2]=*(uint32 *)&dst;
880 for(n=63;n>=0;n--,spr++)
882 if((unsigned int)(scanline-spr->y)>=H) continue;
894 t = (int)scanline-(spr->y);
897 vadr = ((spr->no&1)<<12) + ((spr->no&0xFE)<<4);
899 vadr = (spr->no<<4)+vofs;
914 if(MMC5Hack) C = MMC5SPRVRAMADR(vadr);
915 else C = VRAMADR(vadr);
927 *(uint32 *)&SPRBUF[ns<<2]=*(uint32 *)&dst;
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. */
944 for(n=0;n<(8-ns);n++)
954 static void RefreshSprites098(void)
960 if(!numsprites) return;
962 FCEU_dwmemset(sprlinebuf,0x80808080,256);
964 spr = (SPRB*)SPRBUF+numsprites;
966 for(n=numsprites;n>=0;n--,spr--)
969 //register uint32 pixdata asm ("eax");
970 //register uint8 J, atr;
972 register uint32 pixdata;
973 register uint8 J,atr;
980 pixdata=ppulut1[spr->ca[0]]|ppulut2[spr->ca[1]];
981 J=spr->ca[0]|spr->ca[1];
986 if(n==0 && SpriteBlurp && !(PPU_status&0x40))
991 sphitdata= ((J<<7)&0x80) |
1002 VB = (PALRAM+0x10)+((atr&3)<<2);
1008 if(J&0x80) C[7]=VB[pixdata&3]|0x40;
1010 if(J&0x40) C[6]=VB[pixdata&3]|0x40;
1012 if(J&0x20) C[5]=VB[pixdata&3]|0x40;
1014 if(J&0x10) C[4]=VB[pixdata&3]|0x40;
1016 if(J&0x08) C[3]=VB[pixdata&3]|0x40;
1018 if(J&0x04) C[2]=VB[pixdata&3]|0x40;
1020 if(J&0x02) C[1]=VB[pixdata&3]|0x40;
1022 if(J&0x01) C[0]=VB[pixdata]|0x40;
1024 if(J&0x80) C[0]=VB[pixdata&3]|0x40;
1026 if(J&0x40) C[1]=VB[pixdata&3]|0x40;
1028 if(J&0x20) C[2]=VB[pixdata&3]|0x40;
1030 if(J&0x10) C[3]=VB[pixdata&3]|0x40;
1032 if(J&0x08) C[4]=VB[pixdata&3]|0x40;
1034 if(J&0x04) C[5]=VB[pixdata&3]|0x40;
1036 if(J&0x02) C[6]=VB[pixdata&3]|0x40;
1038 if(J&0x01) C[7]=VB[pixdata]|0x40;
1043 if(J&0x80) C[7]=VB[pixdata&3];
1045 if(J&0x40) C[6]=VB[pixdata&3];
1047 if(J&0x20) C[5]=VB[pixdata&3];
1049 if(J&0x10) C[4]=VB[pixdata&3];
1051 if(J&0x08) C[3]=VB[pixdata&3];
1053 if(J&0x04) C[2]=VB[pixdata&3];
1055 if(J&0x02) C[1]=VB[pixdata&3];
1057 if(J&0x01) C[0]=VB[pixdata];
1059 if(J&0x80) C[0]=VB[pixdata&3];
1061 if(J&0x40) C[1]=VB[pixdata&3];
1063 if(J&0x20) C[2]=VB[pixdata&3];
1065 if(J&0x10) C[3]=VB[pixdata&3];
1067 if(J&0x08) C[4]=VB[pixdata&3];
1069 if(J&0x04) C[5]=VB[pixdata&3];
1071 if(J&0x02) C[6]=VB[pixdata&3];
1073 if(J&0x01) C[7]=VB[pixdata];
1082 static void CopySprites098(uint8 *target)
1084 uint8 n=((PPU[1]&4)^4)<<1;
1090 if(rendis & 1) return; /* User asked to not display sprites. */
1094 uint32 t=*(uint32 *)(sprlinebuf+n);
1101 if(!(t&0x40) || (P[n]&0x40)) // Normal sprite || behind bg sprite
1107 if(!(t&0x4000) || (P[n+1]&0x40)) // Normal sprite || behind bg sprite
1108 P[n+1]=(sprlinebuf+1)[n];
1113 if(!(t&0x400000) || (P[n+2]&0x40)) // Normal sprite || behind bg sprite
1114 P[n+2]=(sprlinebuf+2)[n];
1119 if(!(t&0x40000000) || (P[n+3]&0x40)) // Normal sprite || behind bg sprite
1120 P[n+3]=(sprlinebuf+3)[n];
1123 /* TODO: Simplify */
1126 if(!(t&0x40000000)) // Normal sprite
1128 else if(P[n]&64) // behind bg sprite
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];
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];
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];
1159 if(n) goto loopskie;
1162 void FCEUPPU_Init(void)
1167 void FCEUPPU_Reset(void)
1169 VRAMBuffer=PPU[0]=PPU[1]=PPU_status=PPU[3]=0;
1172 RefreshAddr=TempAddr=0;
1180 void FCEUPPU_Power(void)
1184 memset(NTARAM,0x00,0x800);
1185 memset(PALRAM,0x00,0x20);
1186 memset(SPRAM,0x00,0x100);
1189 for(x=0x2000;x<0x4000;x+=8)
1199 ARead[x+4]=A200x; //A2004;
1208 BWrite[0x4014]=B4014;
1212 void FCEUPPU_Loop(int skip)
1214 uint32 scanlines_per_frame = PAL ? 312 : 262;
1216 if(ppudead) /* Needed for Knight Rider, possibly others. */
1218 //memset(XBuf, 0x80, 256*240);
1219 //X6502_Run(scanlines_per_frame*(256+85));
1221 for (lines=scanlines_per_frame;lines;lines--)
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
1237 if(FCEUGameInfo.type==GIT_NSF)
1244 // Note: this is needed for asm core
1247 X6502_Run(256+85-12);
1248 for (lines=scanlines_per_frame-242-1;lines;lines--)
1257 if(ScreenON || SpriteON)
1259 if(GameHBIRQHook && ((PPU[0]&0x38)!=0x18))
1262 for(x=0;x<42;x++) {PPU_hook(0x2000); PPU_hook(0);}
1267 if(ScreenON || SpriteON)
1269 RefreshAddr=TempAddr;
1270 if(PPU_hook) PPU_hook(RefreshAddr&0x3fff);
1273 /* Clean this stuff up later. */
1280 if(FCEUGameInfo.type==GIT_NSF)
1282 // run scanlines for asm core to fuction
1283 for(scanline=0;scanline<240;scanline++)
1294 PPU_status|=0x20; // Fixes "Bee 52". Does it break anything?
1298 for(scanline=0;scanline<240;scanline++)
1300 if(ScreenON || SpriteON)
1302 if(scanline==y && SpriteON) PPU_status|=0x40;
1303 X6502_Run((scanline==239)?85:(256+85));
1308 for (lines=y;lines;lines--)
1310 if(SpriteON) PPU_status|=0x40; // Quick and very dirty hack.
1311 for (lines=240-y;lines;lines--)
1316 for (lines=240;lines;lines--)
1326 for(scanline=0;scanline<240;) //scanline is incremented in DoLine. Evil. :/
1330 if((PPUViewer) && (scanline == PPUViewScanline)) UpdatePPUView(1);
1334 if(MMC5Hack && (ScreenON || SpriteON)) MMC5_hb(scanline);
1335 for(x=1,max=0,maxref=0;x<7;x++)
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);
1348 } /* else... to if(ppudead) */