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