3e4dde58ec9616c0ef264fb0335a603e317fd046
[fceu.git] / fce.c
1 /* FCE Ultra - NES/Famicom Emulator
2  *
3  * Copyright notice for this file:
4  *  Copyright (C) 1998 BERO
5  *  Copyright (C) 2002 Ben Parnell
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 #include        <stdio.h>
24 #include        <stdlib.h>
25
26 #include        "types.h"
27 #include        "x6502.h"
28 #include        "fce.h"
29 #include        "sound.h"
30 #include        "svga.h"
31 #include        "netplay.h"
32 #include        "general.h"
33 #include        "endian.h"
34 #include        "version.h"
35 #include        "memory.h"
36
37 #include        "cart.h"
38 #include        "nsf.h"
39 #include        "fds.h"
40 #include        "ines.h"
41 #include        "unif.h"
42 #include        "cheat.h"
43
44 #include        "state.h"
45 #include        "video.h"
46 #include        "input.h"
47 #include        "file.h"
48 #include        "crc32.h"
49 #include        "ppu.h"
50
51 #define Pal     (PALRAM)
52
53
54 static void (*RefreshLine)(uint8 *P, uint32 vofs) = NULL;
55 static void PRefreshLine(void);
56
57 static void ResetPPU(void);
58 static void PowerPPU(void);
59
60 uint64 timestampbase=0;
61
62 int MMC5Hack;
63 uint32 MMC5HackVROMMask;
64 uint8 *MMC5HackExNTARAMPtr;
65 uint8 *MMC5HackVROMPTR;
66 uint8 MMC5HackCHRMode=0;
67 uint8 MMC5HackSPMode;
68 uint8 MMC5HackSPScroll;
69 uint8 MMC5HackSPPage;
70
71 uint8 *MMC5SPRVPage[8];
72 uint8 *MMC5BGVPage[8];
73
74
75 uint8 VRAMBuffer,PPUGenLatch;
76
77 uint8 *vnapage[4];
78 uint8 PPUNTARAM;
79 uint8 PPUCHRRAM;
80
81 /* Color deemphasis emulation.  Joy... */
82 static uint8 deemp=0;
83 static int deempcnt[8];
84
85 int tosprite=256;
86
87 FCEUGI FCEUGameInfo;
88 void (*GameInterface)(int h);
89
90 void FP_FASTAPASS(1) (*PPU_hook)(uint32 A);
91
92 void (*GameStateRestore)(int version);
93 void (*GameHBIRQHook)(void);
94
95 readfunc ARead[0x10000];
96 writefunc BWrite[0x10000];
97 static readfunc *AReadG;
98 static writefunc *BWriteG;
99 static int RWWrap=0;
100
101 DECLFW(BNull)
102 {
103
104 }
105
106 DECLFR(ANull)
107 {
108  return(X.DB);
109 }
110
111 int AllocGenieRW(void)
112 {
113  if(!(AReadG=FCEU_malloc(0x8000*sizeof(readfunc))))
114   return 0;
115  if(!(BWriteG=FCEU_malloc(0x8000*sizeof(writefunc))))
116   return 0;
117  RWWrap=1;
118  return 1;
119 }
120
121 void FlushGenieRW(void)
122 {
123  int32 x;
124
125  if(RWWrap)
126  {
127   for(x=0;x<0x8000;x++)
128   {
129    ARead[x+0x8000]=AReadG[x];
130    BWrite[x+0x8000]=BWriteG[x];
131   }
132   free(AReadG);
133   free(BWriteG);
134   AReadG=0;
135   BWriteG=0;
136   RWWrap=0;
137  }
138 }
139
140 readfunc FASTAPASS(1) GetReadHandler(int32 a)
141 {
142   if(a>=0x8000 && RWWrap)
143    return AReadG[a-0x8000];
144   else
145    return ARead[a];
146 }
147
148 void FASTAPASS(3) SetReadHandler(int32 start, int32 end, readfunc func)
149 {
150   int32 x;
151
152   if(!func)
153    func=ANull;
154
155   if(RWWrap)
156    for(x=end;x>=start;x--)
157    {
158     if(x>=0x8000)
159      AReadG[x-0x8000]=func;
160     else
161      ARead[x]=func;
162    }
163   else
164
165    for(x=end;x>=start;x--)
166     ARead[x]=func;
167 }
168
169 writefunc FASTAPASS(1) GetWriteHandler(int32 a)
170 {
171   if(RWWrap && a>=0x8000)
172    return BWriteG[a-0x8000];
173   else
174    return BWrite[a];
175 }
176
177 void FASTAPASS(3) SetWriteHandler(int32 start, int32 end, writefunc func)
178 {
179   int32 x;
180
181   if(!func)
182    func=BNull;
183
184   if(RWWrap)
185    for(x=end;x>=start;x--)
186    {
187     if(x>=0x8000)
188      BWriteG[x-0x8000]=func;
189     else
190      BWrite[x]=func;
191    }
192   else
193    for(x=end;x>=start;x--)
194     BWrite[x]=func;
195 }
196
197 uint8 vtoggle=0;
198 uint8 XOffset=0;
199
200 uint32 TempAddr,RefreshAddr;
201
202
203 /* scanline is equal to the current visible scanline we're on. */
204
205 int scanline;
206 static uint32 scanlines_per_frame;
207
208 uint8 GameMemBlock[131072] __attribute__ ((aligned (4)));
209 uint8 NTARAM[0x800] __attribute__ ((aligned (4)));
210 uint8 PALRAM[0x20] __attribute__ ((aligned (4)));
211 #ifndef ASM_6502
212 uint8 RAM[0x800] __attribute__ ((aligned (4)));
213 #endif
214
215 uint8 PPU[4];
216 uint8 PPUSPL;
217
218 uint8 PAL=0;
219
220
221 #define MMC5BGVRAMADR(V)      &MMC5BGVPage[(V)>>10][(V)]
222 #define VRAMADR(V)      &VPage[(V)>>10][(V)]
223
224 static DECLFW(BRAML)
225 {
226         RAM[A]=V;
227 }
228
229 static DECLFW(BRAMH)
230 {
231         RAM[A&0x7FF]=V;
232 }
233
234 static DECLFR(ARAML)
235 {
236         return RAM[A];
237 }
238
239 static DECLFR(ARAMH)
240 {
241         return RAM[A&0x7FF];
242 }
243
244
245 static DECLFR(A2002)
246 {
247                         uint8 ret;
248                         ret = PPU_status;
249                         vtoggle=0;
250                         PPU_status&=0x7F;
251                         return ret|(PPUGenLatch&0x1F);
252 }
253
254 static DECLFR(A200x)
255 {
256                         return PPUGenLatch;
257 }
258
259 static DECLFR(A2007)
260 {
261                         uint8 ret;
262                         uint32 tmp=RefreshAddr&0x3FFF;
263
264                         PPUGenLatch=ret=VRAMBuffer;
265                         if(PPU_hook) PPU_hook(tmp);
266                         if(tmp<0x2000)
267                         {
268                          VRAMBuffer=VPage[tmp>>10][tmp];
269                         }
270                         else
271                         {
272                          VRAMBuffer=vnapage[(tmp>>10)&0x3][tmp&0x3FF];
273                         }
274
275                         if (INC32) RefreshAddr+=32;
276                         else RefreshAddr++;
277                         if(PPU_hook) PPU_hook(RefreshAddr&0x3fff);
278                         return ret;
279 }
280
281 static DECLFW(B2000)
282 {
283                 PPUGenLatch=V;
284                 PPU[0]=V;
285                 TempAddr&=0xF3FF;
286                 TempAddr|=(V&3)<<10;
287 }
288
289 static DECLFW(B2001)
290 {
291                   PPUGenLatch=V;
292                   PPU[1]=V;
293                   if(V&0xE0)
294                    deemp=V>>5;
295                   //printf("$%04x:$%02x, %d\n",X.PC,V,scanline);
296 }
297
298 static DECLFW(B2002)
299 {
300                  PPUGenLatch=V;
301 }
302
303 static DECLFW(B2003)
304 {
305                 PPUGenLatch=V;
306                 PPU[3]=V;
307                 PPUSPL=V&0x7;
308 }
309
310 static DECLFW(B2004)
311 {
312                 PPUGenLatch=V;
313                 //SPRAM[PPU[3]++]=V;
314                 if(PPUSPL&8)
315                 {
316                  if(PPU[3]>=8)
317                   SPRAM[PPU[3]]=V;
318                 }
319                 else
320                 {
321                  //printf("$%02x:$%02x\n",PPUSPL,V);
322                  SPRAM[PPUSPL]=V;
323                  PPUSPL++;
324                 }
325                 PPU[3]++;
326
327 }
328
329 static DECLFW(B2005)
330 {
331                 uint32 tmp=TempAddr;
332
333                 PPUGenLatch=V;
334                 if (!vtoggle)
335                 {
336                  tmp&=0xFFE0;
337                  tmp|=V>>3;
338                  XOffset=V&7;
339                 }
340                 else
341                 {
342                  tmp&=0x8C1F;
343                  tmp|=((V&~0x7)<<2);
344                  tmp|=(V&7)<<12;
345                 }
346
347                 TempAddr=tmp;
348                 vtoggle^=1;
349 }
350
351 static DECLFW(B2006)
352 {
353                        PPUGenLatch=V;
354                        if(!vtoggle)
355                        {
356                         TempAddr&=0x00FF;
357                         TempAddr|=(V&0x3f)<<8;
358                        }
359                        else
360                        {
361                         TempAddr&=0xFF00;
362                         TempAddr|=V;
363                         RefreshAddr=TempAddr;
364
365                         if(PPU_hook)
366                          PPU_hook(RefreshAddr);
367                        }
368                       vtoggle^=1;
369 }
370
371 static DECLFW(B2007)
372 {
373                         uint32 tmp=RefreshAddr&0x3FFF;
374
375                         PPUGenLatch=V;
376                         if(tmp>=0x3F00)
377                         {
378                         // hmmm....
379                         if(!(tmp&0xf))
380                          PALRAM[0x00]=PALRAM[0x04]=PALRAM[0x08]=PALRAM[0x0C]=
381                          PALRAM[0x10]=PALRAM[0x14]=PALRAM[0x18]=PALRAM[0x1c]=V&0x3f;
382                         else if(tmp&3) PALRAM[(tmp&0x1f)]=V&0x3f;
383                         }
384                         else if(tmp<0x2000)
385                         {
386                           if(PPUCHRRAM&(1<<(tmp>>10)))
387                             VPage[tmp>>10][tmp]=V;
388                         }
389                         else
390                         {
391                          if(PPUNTARAM&(1<<((tmp&0xF00)>>10)))
392                           vnapage[((tmp&0xF00)>>10)][tmp&0x3FF]=V;
393                         }
394                         if (INC32) RefreshAddr+=32;
395                         else RefreshAddr++;
396                         if(PPU_hook) PPU_hook(RefreshAddr&0x3fff);
397 }
398
399 static DECLFW(B4014)
400 {
401         uint32 t=V<<8;
402         int x;
403         for(x=0;x<256;x++)
404          B2004(0x2004,X.DB=ARead[t+x](t+x));
405         X6502_AddCycles(512);
406 }
407
408 void BGRender(uint8 *target)
409 {
410         uint32 tem, vofs;
411         vofs=((PPU[0]&0x10)<<8) | ((RefreshAddr>>12)&7);
412
413         Pal[0]|=64;
414         Pal[4]|=64;
415         Pal[8]|=64;
416         Pal[0xC]|=64;
417         RefreshLine(target-XOffset, vofs);
418         Pal[0]&=63;
419         Pal[4]&=63;
420         Pal[8]&=63;
421         Pal[0xC]&=63;
422
423         if(!(PPU[1]&2))
424         {
425          tem=Pal[0]|0x40;
426          tem|=tem<<8;
427          tem|=tem<<16;
428          *(uint32 *)target=*(uint32 *)(target+4)=tem;
429         }
430 }
431
432 #ifdef FRAMESKIP
433 int FSkip_setting=-1; // auto
434 int FSkip=0;
435 void FCEUI_FrameSkip(int x)
436 {
437  FSkip=x;
438 }
439 #endif
440
441 /*      This is called at the beginning of each visible scanline */
442 static void Loop6502(void)
443 {
444         uint32 tem;
445         int x;
446         uint8 *target=XBuf+scanline*320+32;
447
448         if(ScreenON || SpriteON)
449         {
450          /* PRefreshLine() will not get called on skipped frames.  This
451             could cause a problem, but the solution would be rather complex,
452             due to the current sprite 0 hit code.
453          */
454          #ifdef FRAMESKIP
455          if(!FSkip)
456          {
457          #endif
458           if(ScreenON)
459           {
460            if(scanline>=FSettings.FirstSLine && scanline<=FSettings.LastSLine)
461             BGRender(target);
462            else
463            {
464             if(PPU_hook)
465              PRefreshLine();
466            }
467           }
468           else
469           {
470            tem=Pal[0]|(Pal[0]<<8)|(Pal[0]<<16)|(Pal[0]<<24);
471            tem|=0x40404040;
472            FCEU_dwmemset(target,tem,264);
473           }
474          #ifdef FRAMESKIP
475          }
476          #endif
477          if (SpriteON && scanline)
478           RefreshSprite(target);
479          #ifdef FRAMESKIP
480          if(!FSkip)
481          {
482          #endif
483           if(PPU[1]&0x01)
484           {
485            for(x=63;x>=0;x--)
486             ((uint32 *)target)[x]=((uint32*)target)[x]&0xF0F0F0F0;
487           }
488            if((PPU[1]>>5)==0x7)
489             for(x=63;x>=0;x--)
490              ((uint32 *)target)[x]=(((uint32*)target)[x]&0x3f3f3f3f)|0x40404040;
491            else if(PPU[1]&0xE0)
492             for(x=63;x>=0;x--)
493              ((uint32 *)target)[x]=((uint32*)target)[x]|0xC0C0C0C0;
494            else
495             for(x=63;x>=0;x--)
496              ((uint32 *)target)[x]=((uint32*)target)[x]&0x3f3f3f3f;
497           FCEU_dwmemset(target-  8,0x3f3f3f3f,8);
498           FCEU_dwmemset(target+256,0x3f3f3f3f,8);
499          #ifdef FRAMESKIP
500          }
501          #endif
502         }
503         else
504         {
505          tem=Pal[0]|(Pal[0]<<8)|(Pal[0]<<16)|(Pal[0]<<24);
506          FCEU_dwmemset(target,tem,256);
507         }
508         if(InputScanlineHook)
509          InputScanlineHook(target, scanline);
510 }
511
512 #define PAL(c)  ((c)+cc)
513
514
515 static void PRefreshLine(void)
516 {
517         uint32 vofs;
518         uint8 X1;
519
520         vofs = 0;
521         if (BGAdrHI) vofs = 0x1000;
522
523         vofs+=(RefreshAddr>>12)&7;
524
525         for(X1=33;X1;X1--)
526         {
527                 register uint8 no;
528                 register uint8 zz2;
529                 zz2=(uint8)((RefreshAddr>>10)&3);
530                 PPU_hook(0x2000|(RefreshAddr&0xFFF));
531                 no  = vnapage[zz2][(RefreshAddr&0x3ff)];
532                 PPU_hook((no<<4)+vofs);
533                 if((RefreshAddr&0x1f)==0x1f)
534                  RefreshAddr^=0x41F;
535                 else
536                  RefreshAddr++;
537         }
538 }
539
540 /* This high-level graphics MMC5 emulation code was written
541    for MMC5 carts in "CL" mode.  It's probably not totally
542    correct for carts in "SL" mode.
543    */
544 static void RefreshLine_MMC5Hack1(uint8 *P, uint32 vofs)
545 {
546           int8 tochange, X1;
547
548           tochange=MMC5HackSPMode&0x1F;
549
550           for(X1=33;X1;X1--,P+=8)
551           {
552                 uint8 *C;
553                 uint8 cc,zz,zz2;
554                 uint32 vadr;
555
556                 if((tochange<=0 && MMC5HackSPMode&0x40) ||
557                    (tochange>0 && !(MMC5HackSPMode&0x40)))
558                 {
559                  uint8 xs,ys;
560
561                  xs=33-X1;
562                  ys=((scanline>>3)+MMC5HackSPScroll)&0x1F;
563                  if(ys>=0x1E) ys-=0x1E;
564                  vadr=(MMC5HackExNTARAMPtr[xs|(ys<<5)]<<4)+(vofs&7);
565
566                  C = MMC5HackVROMPTR+vadr;
567                  C += ((MMC5HackSPPage & 0x3f & MMC5HackVROMMask) << 12);
568
569                  cc=MMC5HackExNTARAMPtr[0x3c0+(xs>>2)+((ys&0x1C)<<1)];
570                  cc=((cc >> ((xs&2) + ((ys&0x2)<<1))) &3) <<2;
571                 }
572                 else
573                 {
574                  zz=RefreshAddr&0x1F;
575                  zz2=(RefreshAddr>>10)&3;
576                  vadr=(vnapage[zz2][RefreshAddr&0x3ff]<<4)+vofs;
577                  C = MMC5BGVRAMADR(vadr);
578                  cc=vnapage[zz2][0x3c0+(zz>>2)+((RefreshAddr&0x380)>>4)];
579                  cc=((cc >> ((zz&2) + ((RefreshAddr&0x40)>>4))) &3) <<2;
580                 }
581                 #include "fceline.h"
582
583                 if((RefreshAddr&0x1f)==0x1f)
584                  RefreshAddr^=0x41F;
585                 else
586                  RefreshAddr++;
587                 tochange--;
588           }
589 }
590
591 static void RefreshLine_MMC5Hack2(uint8 *P, uint32 vofs)
592 {
593           int8 tochange, X1;
594
595           tochange=MMC5HackSPMode&0x1F;
596
597           for(X1=33;X1;X1--,P+=8)
598           {
599                 uint8 *C;
600                 uint8 cc;
601                 uint8 zz2;
602                 uint32 vadr;
603
604                 if((tochange<=0 && MMC5HackSPMode&0x40) ||
605                    (tochange>0 && !(MMC5HackSPMode&0x40)))
606                 {
607                  uint8 xs,ys;
608
609                  xs=33-X1;
610                  ys=((scanline>>3)+MMC5HackSPScroll)&0x1F;
611                  if(ys>=0x1E) ys-=0x1E;
612                  vadr=(MMC5HackExNTARAMPtr[xs|(ys<<5)]<<4)+(vofs&7);
613
614                  C = MMC5HackVROMPTR+vadr;
615                  C += ((MMC5HackSPPage & 0x3f & MMC5HackVROMMask) << 12);
616
617                  cc=MMC5HackExNTARAMPtr[0x3c0+(xs>>2)+((ys&0x1C)<<1)];
618                  cc=((cc >> ((xs&2) + ((ys&0x2)<<1))) &3) <<2;
619                 }
620                 else
621                 {
622                  C=MMC5HackVROMPTR;
623                  zz2=(RefreshAddr>>10)&3;
624                  vadr = (vnapage[zz2][RefreshAddr & 0x3ff] << 4) + vofs;
625                  C += (((MMC5HackExNTARAMPtr[RefreshAddr & 0x3ff]) & 0x3f &
626                          MMC5HackVROMMask) << 12) + (vadr & 0xfff);
627                  vadr = (MMC5HackExNTARAMPtr[RefreshAddr & 0x3ff] & 0xC0)>> 4;
628                  cc = vadr;
629                 }
630                 #include "fceline.h"
631                 if((RefreshAddr&0x1f)==0x1f)
632                  RefreshAddr^=0x41F;
633                 else
634                  RefreshAddr++;
635                 tochange--;
636           }
637 }
638
639 static void RefreshLine_MMC5Hack3(uint8 *P, uint32 vofs)
640 {
641           int8 X1;
642
643           for(X1=33;X1;X1--,P+=8)
644           {
645                 uint8 *C;
646                 uint8 cc;
647                 uint8 zz2;
648                 uint32 vadr;
649
650                 C=MMC5HackVROMPTR;
651                 zz2=(RefreshAddr>>10)&3;
652                 vadr = (vnapage[zz2][RefreshAddr & 0x3ff] << 4) + vofs;
653                 C += (((MMC5HackExNTARAMPtr[RefreshAddr & 0x3ff]) & 0x3f &
654                         MMC5HackVROMMask) << 12) + (vadr & 0xfff);
655                 vadr = (MMC5HackExNTARAMPtr[RefreshAddr & 0x3ff] & 0xC0)>> 4;
656                 cc = vadr;
657
658                 #include "fceline.h"
659                 if((RefreshAddr&0x1f)==0x1f)
660                  RefreshAddr^=0x41F;
661                 else
662                  RefreshAddr++;
663           }
664 }
665
666 static void RefreshLine_MMC5Hack4(uint8 *P, uint32 vofs)
667 {
668           int8 X1;
669
670           for(X1=33;X1;X1--,P+=8)
671           {
672                 uint8 *C;
673                 uint8 cc,zz,zz2;
674                 uint32 vadr;
675
676                 zz=RefreshAddr&0x1F;
677                 zz2=(RefreshAddr>>10)&3;
678                 vadr=(vnapage[zz2][RefreshAddr&0x3ff]<<4)+vofs;
679                 C = MMC5BGVRAMADR(vadr);
680                 cc=vnapage[zz2][0x3c0+(zz>>2)+((RefreshAddr&0x380)>>4)];
681                 cc=((cc >> ((zz&2) + ((RefreshAddr&0x40)>>4))) &3) <<2;
682
683                 #include "fceline.h"
684
685                 if((RefreshAddr&0x1f)==0x1f)
686                  RefreshAddr^=0x41F;
687                 else
688                  RefreshAddr++;
689           }
690 }
691
692 static void RefreshLine_PPU_hook(uint8 *P, uint32 vofs)
693 {
694          int8 X1;
695
696          for(X1=33;X1;X1--,P+=8)
697          {
698                 uint8 *C;
699                 uint8 cc,zz,zz2;
700                 uint32 vadr;
701
702                 zz=RefreshAddr&0x1F;
703                 zz2=(RefreshAddr>>10)&3;
704                 PPU_hook(0x2000|(RefreshAddr&0xFFF));
705                 cc=vnapage[zz2][0x3c0+(zz>>2)+((RefreshAddr&0x380)>>4)];
706                 cc=((cc >> ((zz&2) + ((RefreshAddr&0x40)>>4))) &3) <<2;
707                 vadr=(vnapage[zz2][RefreshAddr&0x3ff]<<4)+vofs;
708                 C = VRAMADR(vadr);
709
710                 #include "fceline.h"
711
712                 PPU_hook(vadr);
713
714                 if((RefreshAddr&0x1f)==0x1f)
715                  RefreshAddr^=0x41F;
716                 else
717                  RefreshAddr++;
718          }
719 }
720
721 static void RefreshLine_normal(uint8 *P, uint32 vofs) // vofs is 0x107 max
722 {
723          int8 X1;
724          uint32 rfraddr = RefreshAddr;
725          uint8 *page = vnapage[(rfraddr>>10)&3];
726          uint32 cc2=0;
727
728          if ((rfraddr&0xc)!=0)
729           cc2=*(uint32 *) (page + ((rfraddr&0x380)>>4) + ((rfraddr&0x10)>>2) + 0x3c0);
730
731          for (X1=33;X1;X1--,P+=8)
732          {
733                 uint8 cc,*C;
734                 uint32 vadr;
735
736                 vadr=(page[rfraddr&0x3ff]<<4)+vofs;
737                 C = VRAMADR(vadr);
738                 if ((rfraddr&0xc)==0)
739                  cc2=*(uint32 *) (page + ((rfraddr&0x380)>>4) + ((rfraddr&0x10)>>2) + 0x3c0);
740                 cc=((cc2 >> ((rfraddr&2) + ((rfraddr&0x40)>>4) + ((rfraddr&0xc)<<1))) & 3) << 2;
741
742                 #include "fceline.h"
743
744                 if((rfraddr&0x1f)==0x1f) {
745                  rfraddr^=0x41F;
746                  page = vnapage[(rfraddr>>10)&3];
747                 } else
748                  rfraddr++;
749          }
750          RefreshAddr = rfraddr;
751 }
752
753 static void SetRefreshLine(void)
754 {
755         if(MMC5Hack && geniestage!=1)
756         {
757          if(MMC5HackCHRMode==0 && (MMC5HackSPMode&0x80))
758          {
759                  if (RefreshLine != RefreshLine_MMC5Hack1) printf("set refr RefreshLine_MMC5Hack1\n");
760                  RefreshLine = RefreshLine_MMC5Hack1;
761          }
762          else if(MMC5HackCHRMode==1 && (MMC5HackSPMode&0x80))
763          {
764                 if (RefreshLine != RefreshLine_MMC5Hack2) printf("set refr RefreshLine_MMC5Hack2\n");
765                  RefreshLine = RefreshLine_MMC5Hack2;
766          }
767          else if(MMC5HackCHRMode==1)
768          {
769                 if (RefreshLine != RefreshLine_MMC5Hack3) printf("set refr RefreshLine_MMC5Hack3\n");
770                  RefreshLine = RefreshLine_MMC5Hack3;
771          }
772          else
773          {
774                 if (RefreshLine != RefreshLine_MMC5Hack4) printf("set refr RefreshLine_MMC5Hack4\n");
775                  RefreshLine = RefreshLine_MMC5Hack4;
776          }
777         }       // End if(MMC5Hack)
778         else if(PPU_hook)
779         {
780                 if (RefreshLine != RefreshLine_PPU_hook) printf("set refr RefreshLine_PPU_hook\n");
781                 RefreshLine = RefreshLine_PPU_hook;
782         }
783         else
784         {
785                 if (RefreshLine != RefreshLine_normal) printf("set refr RefreshLine_normal\n");
786                 RefreshLine = RefreshLine_normal;
787         }
788 }
789
790 //static INLINE
791 void Fixit2(void)
792 {
793    if(ScreenON || SpriteON)
794    {
795     uint32 rad=RefreshAddr;
796     rad&=0xFBE0;
797     rad|=TempAddr&0x041f;
798     RefreshAddr=rad;
799     //PPU_hook(RefreshAddr,-1);
800    }
801 }
802
803 //static INLINE
804 void Fixit1(void)
805 {
806    if(ScreenON || SpriteON)
807    {
808     uint32 rad=RefreshAddr;
809
810     if((rad&0x7000)==0x7000)
811     {
812      rad^=0x7000;
813      if((rad&0x3E0)==0x3A0)
814      {
815       rad^=0x3A0;
816       rad^=0x800;
817      }
818      else
819      {
820       if((rad&0x3E0)==0x3e0)
821        rad^=0x3e0;
822       else rad+=0x20;
823      }
824     }
825     else
826      rad+=0x1000;
827     RefreshAddr=rad;
828     //PPU_hook(RefreshAddr,-1);
829    }
830 }
831
832 //#define NEW_TRY
833
834 /*      This is called at the beginning of all h-blanks on visible lines. */
835 #ifndef NEW_TRY
836 static void DoHBlank(void)
837 {
838  if(ScreenON || SpriteON)
839   FetchSpriteData();
840  if(GameHBIRQHook && (ScreenON || SpriteON))
841  {
842   X6502_Run(12);
843   GameHBIRQHook();
844   X6502_Run(25-12);
845   Fixit2();
846   X6502_Run(85-25);
847  }
848  else
849  {
850   X6502_Run(25);        // Tried 65, caused problems with Slalom(maybe others)
851   Fixit2();
852   X6502_Run(85-25);
853  }
854  //PPU_hook(0,-1);
855  //fprintf(stderr,"%3d: $%04x\n",scanline,RefreshAddr);
856 }
857 #endif
858
859
860 // ============================//
861 // end of new code
862 // ===========================//
863
864 void ResetMapping(void)
865 {
866         int x;
867
868         SetReadHandler(0x0000,0xFFFF,ANull);
869         SetWriteHandler(0x0000,0xFFFF,BNull);
870
871         SetReadHandler(0,0x7FF,ARAML);
872         SetWriteHandler(0,0x7FF,BRAML);
873
874         SetReadHandler(0x800,0x1FFF,ARAMH);  /* Part of a little */
875         SetWriteHandler(0x800,0x1FFF,BRAMH); /* hack for a small speed boost. */
876
877         for(x=0x2000;x<0x4000;x+=8)
878         {
879          ARead[x]=A200x;
880          BWrite[x]=B2000;
881          ARead[x+1]=A200x;
882          BWrite[x+1]=B2001;
883          ARead[x+2]=A2002;
884          BWrite[x+2]=B2002;
885          ARead[x+3]=A200x;
886          BWrite[x+3]=B2003;
887          ARead[x+4]=A200x;
888          BWrite[x+4]=B2004;
889          ARead[x+5]=A200x;
890          BWrite[x+5]=B2005;
891          ARead[x+6]=A200x;
892          BWrite[x+6]=B2006;
893          ARead[x+7]=A2007;
894          BWrite[x+7]=B2007;
895         }
896
897         BWrite[0x4014]=B4014;
898         SetNESSoundMap();
899         InitializeInput();
900 }
901
902 int GameLoaded=0;
903 void CloseGame(void)
904 {
905  if(GameLoaded)
906  {
907   if(FCEUGameInfo.type!=GIT_NSF)
908    FlushGameCheats();
909   #ifdef NETWORK
910   if(FSettings.NetworkPlay) KillNetplay();
911   #endif
912   GameInterface(GI_CLOSE);
913   CloseGenie();
914   GameLoaded=0;
915  }
916 }
917
918 void ResetGameLoaded(void)
919 {
920         if(GameLoaded) CloseGame();
921         GameStateRestore=0;
922         PPU_hook=0;
923         GameHBIRQHook=0;
924         GameExpSound.Fill=0;
925         GameExpSound.RChange=0;
926         if(GameExpSound.Kill)
927          GameExpSound.Kill();
928         GameExpSound.Kill=0;
929         MapIRQHook=0;
930         MMC5Hack=0;
931         PAL&=1;
932         pale=0;
933
934         FCEUGameInfo.name=0;
935         FCEUGameInfo.type=GIT_CART;
936         FCEUGameInfo.vidsys=GIV_USER;
937         FCEUGameInfo.input[0]=FCEUGameInfo.input[1]=-1;
938         FCEUGameInfo.inputfc=-1;
939 }
940
941 FCEUGI *FCEUI_LoadGame(char *name)
942 {
943         int fp;
944
945         Exit=1;
946         ResetGameLoaded();
947
948         fp=FCEU_fopen(name,"rb");
949         if(!fp)
950         {
951          FCEU_PrintError("Error opening \"%s\"!",name);
952          return 0;
953         }
954
955         GetFileBase(name);
956         if(iNESLoad(name,fp))
957          goto endlseq;
958         if(NSFLoad(fp))
959          goto endlseq;
960         if(FDSLoad(name,fp))
961          goto endlseq;
962         if(UNIFLoad(name,fp))
963          goto endlseq;
964
965         FCEU_PrintError("An error occurred while loading the file.");
966         FCEU_fclose(fp);
967         return 0;
968
969         endlseq:
970         FCEU_fclose(fp);
971         GameLoaded=1;
972
973         FCEU_ResetVidSys();
974         if(FCEUGameInfo.type!=GIT_NSF)
975          if(FSettings.GameGenie)
976           OpenGenie();
977
978         PowerNES();
979         #ifdef NETWORK
980         if(FSettings.NetworkPlay) InitNetplay();
981         #endif
982         SaveStateRefresh();
983         if(FCEUGameInfo.type!=GIT_NSF)
984         {
985          LoadGamePalette();
986          LoadGameCheats();
987         }
988
989         FCEU_ResetPalette();
990         Exit=0;
991         return(&FCEUGameInfo);
992 }
993
994
995 void FCEU_ResetVidSys(void)
996 {
997  int w;
998
999  if(FCEUGameInfo.vidsys==GIV_NTSC)
1000   w=0;
1001  else if(FCEUGameInfo.vidsys==GIV_PAL)
1002   w=1;
1003  else
1004   w=FSettings.PAL;
1005
1006  if(w)
1007  {
1008   PAL=1;
1009   scanlines_per_frame=312;
1010   FSettings.FirstSLine=FSettings.UsrFirstSLine[1];
1011   FSettings.LastSLine=FSettings.UsrLastSLine[1];
1012  }
1013  else
1014  {
1015   PAL=0;
1016   scanlines_per_frame=262;
1017   FSettings.FirstSLine=FSettings.UsrFirstSLine[0];
1018   FSettings.LastSLine=FSettings.UsrLastSLine[0];
1019  }
1020  printf("PAL = %i\n", PAL);
1021  SetSoundVariables();
1022 }
1023
1024 int FCEUI_Initialize(void)
1025 {
1026         if(!InitVirtualVideo())
1027          return 0;
1028         memset(&FSettings,0,sizeof(FSettings));
1029         FSettings.UsrFirstSLine[0]=8;
1030         FSettings.UsrFirstSLine[1]=0;
1031         FSettings.UsrLastSLine[0]=FSettings.UsrLastSLine[1]=239;
1032         FSettings.SoundVolume=65535;    // 100%
1033         return 1;
1034 }
1035
1036 #define harko 0xe //0x9
1037 static INLINE void Thingo(void)
1038 {
1039    Loop6502();
1040 #ifndef NEW_TRY
1041
1042    if(tosprite>=256)
1043    {
1044     X6502_Run(256-harko);
1045     Fixit1();
1046     X6502_Run(harko);
1047    }
1048    else
1049    {
1050     if(tosprite<=240)
1051     {
1052      X6502_Run(tosprite);
1053      PPU[2]|=0x40;
1054      X6502_Run(256-tosprite-harko);
1055      Fixit1();
1056      X6502_Run(harko);
1057     }
1058     else
1059     {
1060      X6502_Run(256-harko);
1061      Fixit1();
1062      X6502_Run(tosprite-(256-harko));
1063      PPU[2]|=0x40;
1064      X6502_Run(256-tosprite);
1065     }
1066     tosprite=256;
1067    }
1068    DoHBlank();
1069 #else
1070    X6502_Run_scanline();
1071 #endif
1072 }
1073 #undef harko
1074
1075 void EmLoop(void)
1076 {
1077  for(;;)
1078  {
1079         //extern int asdc;
1080         //printf("asdc: %i\n", asdc);
1081         //asdc=0;
1082   ApplyPeriodicCheats();
1083   X6502_Run(256+85);
1084
1085   PPU[2]|=0x80;
1086   PPU[3]=PPUSPL=0;             /* Not sure if this is correct.  According
1087                                   to Matt Conte and my own tests, it is.  Timing is probably
1088                                   off, though.  NOTE:  Not having this here
1089                                   breaks a Super Donkey Kong game. */
1090
1091   X6502_Run(12);                /* I need to figure out the true nature and length
1092                                    of this delay.
1093                                 */
1094   if(FCEUGameInfo.type==GIT_NSF)
1095    TriggerNMINSF();
1096   else if(VBlankON)
1097    TriggerNMI();
1098
1099   X6502_Run((scanlines_per_frame-242)*(256+85)-12);
1100
1101   PPU_status&=0x1f;
1102
1103   X6502_Run(256);
1104   {
1105    static int kook=0;
1106    if(ScreenON || SpriteON)
1107     if(GameHBIRQHook)
1108      GameHBIRQHook();
1109
1110    X6502_Run(85-kook);
1111    kook=(kook+1)&1;
1112   }
1113
1114   if(ScreenON || SpriteON)
1115   {
1116    RefreshAddr=TempAddr;
1117    if(PPU_hook) PPU_hook(RefreshAddr&0x3fff);
1118   }
1119   if(FCEUGameInfo.type==GIT_NSF)
1120   {
1121    X6502_Run((256+85)*240);
1122   }
1123   else
1124   {
1125    int x,max,maxref;
1126
1127    deemp=PPU[1]>>5;
1128    SetRefreshLine();
1129    for(scanline=0;scanline<240;scanline++)
1130    {
1131     deempcnt[deemp]++;
1132     Thingo();
1133    }
1134    for(x=1,max=0,maxref=0;x<7;x++)
1135    {
1136     if(deempcnt[x]>max)
1137     {
1138      max=deempcnt[x];
1139      maxref=x;
1140     }
1141     deempcnt[x]=0;
1142    }
1143    //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);
1144    //memset(deempcnt,0,sizeof(deempcnt));
1145    SetNESDeemph(maxref,0);
1146   }
1147
1148   {
1149    int ssize;
1150
1151    ssize=FlushEmulateSound();
1152
1153    #ifdef FRAMESKIP
1154    if(FSkip)
1155    {
1156     FCEU_PutImageDummy();
1157     FSkip--;
1158     FCEUD_Update(0,WaveFinalMono,ssize);
1159    }
1160    else
1161    #endif
1162    {
1163     FCEU_PutImage();
1164     FCEUD_Update(XBuf+8,WaveFinalMono,ssize);
1165    }
1166    UpdateInput();
1167   }
1168
1169   if(Exit)
1170   {
1171    CloseGame();
1172    break;
1173   }
1174
1175  }
1176 }
1177
1178 #ifdef FPS
1179 #include <sys/time.h>
1180 uint64 frcount;
1181 #endif
1182 void FCEUI_Emulate(void)
1183 {
1184         #ifdef FPS
1185         uint64 starttime,end;
1186         struct timeval tv;
1187         frcount=0;
1188         gettimeofday(&tv,0);
1189         starttime=((uint64)tv.tv_sec*1000000)+tv.tv_usec;
1190         #endif
1191         EmLoop();
1192
1193         #ifdef FPS
1194         // Probably won't work well on Windows port; for
1195         // debugging/speed testing.
1196         {
1197          uint64 w;
1198          int i,frac;
1199          gettimeofday(&tv,0);
1200          end=((uint64)tv.tv_sec*1000000)+tv.tv_usec;
1201          w=frcount*10000000000LL/(end-starttime);
1202          i=w/10000;
1203          frac=w-i*10000;
1204          printf("Average FPS: %d.%04d\n",i,frac);
1205         }
1206         #endif
1207
1208 }
1209
1210 void FCEUI_CloseGame(void)
1211 {
1212         Exit=1;
1213 }
1214
1215 static void ResetPPU(void)
1216 {
1217         VRAMBuffer=PPU[0]=PPU[1]=PPU[2]=PPU[3]=0;
1218         PPUSPL=0;
1219         PPUGenLatch=0;
1220         RefreshAddr=TempAddr=0;
1221         vtoggle = 0;
1222 }
1223
1224 static void PowerPPU(void)
1225 {
1226         memset(NTARAM,0x00,0x800);
1227         memset(PALRAM,0x00,0x20);
1228         memset(SPRAM,0x00,0x100);
1229         ResetPPU();
1230 }
1231
1232 void ResetNES(void)
1233 {
1234         if(!GameLoaded || (FCEUGameInfo.type==GIT_NSF)) return;
1235         GameInterface(GI_RESETM2);
1236         ResetSound();
1237         ResetPPU();
1238         X6502_Reset();
1239 }
1240
1241 void PowerNES(void)
1242 {
1243         if(!GameLoaded) return;
1244
1245         FCEU_CheatResetRAM();
1246         FCEU_CheatAddRAM(2,0,RAM);
1247
1248         GeniePower();
1249
1250         memset(RAM,0x00,0x800);
1251         ResetMapping();
1252         GameInterface(GI_POWER);
1253         PowerSound();
1254         PowerPPU();
1255         timestampbase=0;
1256         X6502_Power();
1257 }
1258