gpfce patch
[fceu.git] / fce.c
CommitLineData
c62d2810 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"
5232c20c 49#include "ppu.h"
c62d2810 50
51#define Pal (PALRAM)
52
5232c20c 53
c62d2810 54static void FASTAPASS(1) RefreshLine(uint8 *target);
55static void PRefreshLine(void);
5232c20c 56
c62d2810 57static void ResetPPU(void);
58static void PowerPPU(void);
59
60uint64 timestampbase=0;
61
62int MMC5Hack;
63uint32 MMC5HackVROMMask;
64uint8 *MMC5HackExNTARAMPtr;
65uint8 *MMC5HackVROMPTR;
66uint8 MMC5HackCHRMode=0;
67uint8 MMC5HackSPMode;
68uint8 MMC5HackSPScroll;
69uint8 MMC5HackSPPage;
70
71uint8 *MMC5SPRVPage[8];
72uint8 *MMC5BGVPage[8];
73
74
75uint8 VRAMBuffer,PPUGenLatch;
76
77uint8 *vnapage[4];
78uint8 PPUNTARAM;
79uint8 PPUCHRRAM;
80
81/* Color deemphasis emulation. Joy... */
82static uint8 deemp=0;
83static int deempcnt[8];
84
5232c20c 85int tosprite=256;
c62d2810 86
87FCEUGI FCEUGameInfo;
88void (*GameInterface)(int h);
89
90void FP_FASTAPASS(1) (*PPU_hook)(uint32 A);
91
92void (*GameStateRestore)(int version);
93void (*GameHBIRQHook)(void);
94
95readfunc ARead[0x10000];
96writefunc BWrite[0x10000];
97static readfunc *AReadG;
98static writefunc *BWriteG;
99static int RWWrap=0;
100
101DECLFW(BNull)
102{
103
104}
105
106DECLFR(ANull)
107{
108 return(X.DB);
109}
110
111int 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
121void 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
140readfunc FASTAPASS(1) GetReadHandler(int32 a)
141{
142 if(a>=0x8000 && RWWrap)
143 return AReadG[a-0x8000];
144 else
145 return ARead[a];
146}
147
148void 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
169writefunc FASTAPASS(1) GetWriteHandler(int32 a)
170{
171 if(RWWrap && a>=0x8000)
172 return BWriteG[a-0x8000];
173 else
174 return BWrite[a];
175}
176
177void 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
197uint8 vtoggle=0;
198uint8 XOffset=0;
199
200uint32 TempAddr,RefreshAddr;
201
c62d2810 202
203/* scanline is equal to the current visible scanline we're on. */
204
205int scanline;
206static uint32 scanlines_per_frame;
207
208uint8 PPU[4];
209uint8 PPUSPL;
210
211uint8 GameMemBlock[131072];
5232c20c 212uint8 NTARAM[0x800],PALRAM[0x20];
c62d2810 213uint8 RAM[0x800];
214
215uint8 PAL=0;
216
5232c20c 217
c62d2810 218#define MMC5BGVRAMADR(V) &MMC5BGVPage[(V)>>10][(V)]
219#define VRAMADR(V) &VPage[(V)>>10][(V)]
220
221static DECLFW(BRAML)
222{
223 RAM[A]=V;
224}
225
226static DECLFW(BRAMH)
227{
228 RAM[A&0x7FF]=V;
229}
230
231static DECLFR(ARAML)
232{
233 return RAM[A];
234}
235
236static DECLFR(ARAMH)
237{
238 return RAM[A&0x7FF];
239}
240
241
242static DECLFR(A2002)
243{
244 uint8 ret;
245 ret = PPU_status;
246 vtoggle=0;
247 PPU_status&=0x7F;
248 return ret|(PPUGenLatch&0x1F);
249}
250
251static DECLFR(A200x)
252{
253 return PPUGenLatch;
254}
255
256static 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
278static DECLFW(B2000)
279{
280 PPUGenLatch=V;
281 PPU[0]=V;
282 TempAddr&=0xF3FF;
283 TempAddr|=(V&3)<<10;
284}
285
286static 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
295static DECLFW(B2002)
296{
297 PPUGenLatch=V;
298}
299
300static DECLFW(B2003)
301{
302 PPUGenLatch=V;
303 PPU[3]=V;
304 PPUSPL=V&0x7;
305}
306
307static 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]++;
5232c20c 323
c62d2810 324}
325
326static 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
348static 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
368static 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
396static 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
5232c20c 405void BGRender(uint8 *target)
c62d2810 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
5232c20c 418extern int framesRendered;
419int FSkip=0;
c62d2810 420void FCEUI_FrameSkip(int x)
421{
422 FSkip=x;
423}
424#endif
425
426/* This is called at the beginning of each visible scanline */
427static 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
499static 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) */
525static void FASTAPASS(1) RefreshLine(uint8 *target)
526{
527 uint32 vofs;
528 int X1;
5232c20c 529 uint8 *P=target;
c62d2810 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;
5232c20c 556 uint8 cc,zz,zz2;
c62d2810 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;
5232c20c 602 uint8 cc;
603 uint8 zz2;
c62d2810 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;
5232c20c 646 uint8 cc;
647 uint8 zz2;
c62d2810 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;
5232c20c 670 uint8 cc,zz,zz2;
c62d2810 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;
5232c20c 695 uint8 cc,zz,zz2;
c62d2810 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;
5232c20c 721 uint8 cc,zz,zz2;
c62d2810 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
747static 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
759static 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. */
788static 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
c62d2810 810
c62d2810 811
c62d2810 812
c62d2810 813
c62d2810 814
c62d2810 815
c62d2810 816
c62d2810 817
c62d2810 818
c62d2810 819
c62d2810 820
c62d2810 821
c62d2810 822
c62d2810 823
c62d2810 824
c62d2810 825
5232c20c 826// ============================//
827// end of new code
828// ===========================//
c62d2810 829
830void 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
868int GameLoaded=0;
869void 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
884void 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
907FCEUGI *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
961void 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
989int 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;
5232c20c 997 FSettings.SoundVolume=65535; // 100%
c62d2810 998 return 1;
999}
1000
1001#define harko 0xe //0x9
1002static 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
1036void 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--;
5232c20c 1113 framesRendered++;
1114 FCEUD_Update(0,WaveFinalMono,ssize);
c62d2810 1115 }
1116 else
1117 #endif
1118 {
1119 FCEU_PutImage();
5232c20c 1120 FCEUD_Update(XBuf+8,WaveFinalMono,ssize);
c62d2810 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>
1136uint64 frcount;
1137#endif
1138void 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
1166void FCEUI_CloseGame(void)
1167{
1168 Exit=1;
1169}
1170
1171static 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
1180static void PowerPPU(void)
1181{
1182 memset(NTARAM,0x00,0x800);
1183 memset(PALRAM,0x00,0x20);
1184 memset(SPRAM,0x00,0x100);
1185 ResetPPU();
1186}
1187
1188void ResetNES(void)
1189{
1190 if(!GameLoaded || (FCEUGameInfo.type==GIT_NSF)) return;
1191 GameInterface(GI_RESETM2);
1192 ResetSound();
1193 ResetPPU();
1194 X6502_Reset();
1195}
1196
1197void 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