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