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