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