mmuhack, 6502 tweaks, fskip
[fceu.git] / fce.c
CommitLineData
c62d2810 1/* FCE Ultra - NES/Famicom Emulator
2 *
3 * Copyright notice for this file:
15300263 4 * Copyright (C) 1998 BERO
c62d2810 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
4a1bf31b 54static void (*RefreshLine)(uint8 *P, uint32 vofs) = NULL;
c62d2810 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;
15300263 66uint8 MMC5HackCHRMode=0;
c62d2810 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
9115e7d2 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
c62d2810 213uint8 PPU[4];
214uint8 PPUSPL;
215
c62d2810 216uint8 PAL=0;
217
5232c20c 218
c62d2810 219#define MMC5BGVRAMADR(V) &MMC5BGVPage[(V)>>10][(V)]
220#define VRAMADR(V) &VPage[(V)>>10][(V)]
15300263 221
c62d2810 222static DECLFW(BRAML)
15300263 223{
c62d2810 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
15300263 242
c62d2810 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);
15300263 264 if(tmp<0x2000)
c62d2810 265 {
266 VRAMBuffer=VPage[tmp>>10][tmp];
267 }
15300263 268 else
c62d2810 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]++;
5232c20c 324
c62d2810 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)
15300263 370{
c62d2810 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
15300263 388 {
c62d2810 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)
15300263 398{
c62d2810 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
5232c20c 406void BGRender(uint8 *target)
c62d2810 407{
4a1bf31b 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
c62d2810 421 if(!(PPU[1]&2))
422 {
9115e7d2 423 tem=Pal[0]|0x40;
424 tem|=tem<<8;
425 tem|=tem<<16;
c62d2810 426 *(uint32 *)target=*(uint32 *)(target+4)=tem;
427 }
428}
429
430#ifdef FRAMESKIP
15300263 431int FSkip_setting=-1; // auto
5232c20c 432int FSkip=0;
c62d2810 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;
937bf65b 444 uint8 *target=XBuf+scanline*320+32;
c62d2810 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)
15300263 482 {
c62d2810 483 for(x=63;x>=0;x--)
937bf65b 484 ((uint32 *)target)[x]=((uint32*)target)[x]&0xF0F0F0F0;
c62d2810 485 }
486 if((PPU[1]>>5)==0x7)
487 for(x=63;x>=0;x--)
937bf65b 488 ((uint32 *)target)[x]=(((uint32*)target)[x]&0x3f3f3f3f)|0x40404040;
c62d2810 489 else if(PPU[1]&0xE0)
490 for(x=63;x>=0;x--)
937bf65b 491 ((uint32 *)target)[x]=((uint32*)target)[x]|0xC0C0C0C0;
c62d2810 492 else
493 for(x=63;x>=0;x--)
937bf65b 494 ((uint32 *)target)[x]=((uint32*)target)[x]&0x3f3f3f3f;
495 FCEU_dwmemset(target- 8,0x3f3f3f3f,8);
496 FCEU_dwmemset(target+256,0x3f3f3f3f,8);
c62d2810 497 #ifdef FRAMESKIP
498 }
499 #endif
500 }
501 else
502 {
503 tem=Pal[0]|(Pal[0]<<8)|(Pal[0]<<16)|(Pal[0]<<24);
504 FCEU_dwmemset(target,tem,256);
15300263 505 }
c62d2810 506 if(InputScanlineHook)
507 InputScanlineHook(target, scanline);
508}
509
510#define PAL(c) ((c)+cc)
511
512
513static void PRefreshLine(void)
514{
515 uint32 vofs;
516 uint8 X1;
517
518 vofs = 0;
519 if (BGAdrHI) vofs = 0x1000;
520
521 vofs+=(RefreshAddr>>12)&7;
522
523 for(X1=33;X1;X1--)
524 {
525 register uint8 no;
526 register uint8 zz2;
527 zz2=(uint8)((RefreshAddr>>10)&3);
528 PPU_hook(0x2000|(RefreshAddr&0xFFF));
529 no = vnapage[zz2][(RefreshAddr&0x3ff)];
530 PPU_hook((no<<4)+vofs);
531 if((RefreshAddr&0x1f)==0x1f)
532 RefreshAddr^=0x41F;
533 else
534 RefreshAddr++;
535 }
536}
537
4a1bf31b 538/* This high-level graphics MMC5 emulation code was written
539 for MMC5 carts in "CL" mode. It's probably not totally
540 correct for carts in "SL" mode.
541 */
542static void RefreshLine_MMC5Hack1(uint8 *P, uint32 vofs)
c62d2810 543{
4a1bf31b 544 int8 tochange, X1;
c62d2810 545
546 tochange=MMC5HackSPMode&0x1F;
547
548 for(X1=33;X1;X1--,P+=8)
549 {
550 uint8 *C;
5232c20c 551 uint8 cc,zz,zz2;
c62d2810 552 uint32 vadr;
553
15300263 554 if((tochange<=0 && MMC5HackSPMode&0x40) ||
c62d2810 555 (tochange>0 && !(MMC5HackSPMode&0x40)))
556 {
557 uint8 xs,ys;
558
15300263 559 xs=33-X1;
c62d2810 560 ys=((scanline>>3)+MMC5HackSPScroll)&0x1F;
561 if(ys>=0x1E) ys-=0x1E;
562 vadr=(MMC5HackExNTARAMPtr[xs|(ys<<5)]<<4)+(vofs&7);
563
564 C = MMC5HackVROMPTR+vadr;
565 C += ((MMC5HackSPPage & 0x3f & MMC5HackVROMMask) << 12);
566
567 cc=MMC5HackExNTARAMPtr[0x3c0+(xs>>2)+((ys&0x1C)<<1)];
568 cc=((cc >> ((xs&2) + ((ys&0x2)<<1))) &3) <<2;
569 }
570 else
571 {
572 zz=RefreshAddr&0x1F;
573 zz2=(RefreshAddr>>10)&3;
574 vadr=(vnapage[zz2][RefreshAddr&0x3ff]<<4)+vofs;
575 C = MMC5BGVRAMADR(vadr);
576 cc=vnapage[zz2][0x3c0+(zz>>2)+((RefreshAddr&0x380)>>4)];
577 cc=((cc >> ((zz&2) + ((RefreshAddr&0x40)>>4))) &3) <<2;
578 }
579 #include "fceline.h"
580
581 if((RefreshAddr&0x1f)==0x1f)
582 RefreshAddr^=0x41F;
583 else
584 RefreshAddr++;
585 tochange--;
586 }
4a1bf31b 587}
588
589static void RefreshLine_MMC5Hack2(uint8 *P, uint32 vofs)
590{
591 int8 tochange, X1;
c62d2810 592
593 tochange=MMC5HackSPMode&0x1F;
594
595 for(X1=33;X1;X1--,P+=8)
596 {
597 uint8 *C;
5232c20c 598 uint8 cc;
599 uint8 zz2;
c62d2810 600 uint32 vadr;
601
602 if((tochange<=0 && MMC5HackSPMode&0x40) ||
603 (tochange>0 && !(MMC5HackSPMode&0x40)))
604 {
605 uint8 xs,ys;
606
15300263 607 xs=33-X1;
c62d2810 608 ys=((scanline>>3)+MMC5HackSPScroll)&0x1F;
609 if(ys>=0x1E) ys-=0x1E;
610 vadr=(MMC5HackExNTARAMPtr[xs|(ys<<5)]<<4)+(vofs&7);
611
612 C = MMC5HackVROMPTR+vadr;
613 C += ((MMC5HackSPPage & 0x3f & MMC5HackVROMMask) << 12);
614
615 cc=MMC5HackExNTARAMPtr[0x3c0+(xs>>2)+((ys&0x1C)<<1)];
616 cc=((cc >> ((xs&2) + ((ys&0x2)<<1))) &3) <<2;
617 }
618 else
619 {
620 C=MMC5HackVROMPTR;
621 zz2=(RefreshAddr>>10)&3;
622 vadr = (vnapage[zz2][RefreshAddr & 0x3ff] << 4) + vofs;
623 C += (((MMC5HackExNTARAMPtr[RefreshAddr & 0x3ff]) & 0x3f &
624 MMC5HackVROMMask) << 12) + (vadr & 0xfff);
625 vadr = (MMC5HackExNTARAMPtr[RefreshAddr & 0x3ff] & 0xC0)>> 4;
626 cc = vadr;
627 }
628 #include "fceline.h"
629 if((RefreshAddr&0x1f)==0x1f)
630 RefreshAddr^=0x41F;
631 else
632 RefreshAddr++;
633 tochange--;
634 }
4a1bf31b 635}
636
637static void RefreshLine_MMC5Hack3(uint8 *P, uint32 vofs)
638{
639 int8 X1;
c62d2810 640
c62d2810 641 for(X1=33;X1;X1--,P+=8)
642 {
15300263 643 uint8 *C;
5232c20c 644 uint8 cc;
645 uint8 zz2;
15300263 646 uint32 vadr;
c62d2810 647
648 C=MMC5HackVROMPTR;
649 zz2=(RefreshAddr>>10)&3;
650 vadr = (vnapage[zz2][RefreshAddr & 0x3ff] << 4) + vofs;
15300263 651 C += (((MMC5HackExNTARAMPtr[RefreshAddr & 0x3ff]) & 0x3f &
c62d2810 652 MMC5HackVROMMask) << 12) + (vadr & 0xfff);
653 vadr = (MMC5HackExNTARAMPtr[RefreshAddr & 0x3ff] & 0xC0)>> 4;
654 cc = vadr;
655
656 #include "fceline.h"
657 if((RefreshAddr&0x1f)==0x1f)
658 RefreshAddr^=0x41F;
659 else
660 RefreshAddr++;
661 }
4a1bf31b 662}
663
664static void RefreshLine_MMC5Hack4(uint8 *P, uint32 vofs)
665{
666 int8 X1;
667
c62d2810 668 for(X1=33;X1;X1--,P+=8)
669 {
670 uint8 *C;
5232c20c 671 uint8 cc,zz,zz2;
c62d2810 672 uint32 vadr;
673
674 zz=RefreshAddr&0x1F;
675 zz2=(RefreshAddr>>10)&3;
676 vadr=(vnapage[zz2][RefreshAddr&0x3ff]<<4)+vofs;
677 C = MMC5BGVRAMADR(vadr);
678 cc=vnapage[zz2][0x3c0+(zz>>2)+((RefreshAddr&0x380)>>4)];
679 cc=((cc >> ((zz&2) + ((RefreshAddr&0x40)>>4))) &3) <<2;
680
681 #include "fceline.h"
15300263 682
c62d2810 683 if((RefreshAddr&0x1f)==0x1f)
684 RefreshAddr^=0x41F;
685 else
686 RefreshAddr++;
15300263 687 }
4a1bf31b 688}
689
690static void RefreshLine_PPU_hook(uint8 *P, uint32 vofs)
691{
692 int8 X1;
c62d2810 693
c62d2810 694 for(X1=33;X1;X1--,P+=8)
695 {
15300263 696 uint8 *C;
5232c20c 697 uint8 cc,zz,zz2;
15300263 698 uint32 vadr;
c62d2810 699
700 zz=RefreshAddr&0x1F;
701 zz2=(RefreshAddr>>10)&3;
702 PPU_hook(0x2000|(RefreshAddr&0xFFF));
703 cc=vnapage[zz2][0x3c0+(zz>>2)+((RefreshAddr&0x380)>>4)];
704 cc=((cc >> ((zz&2) + ((RefreshAddr&0x40)>>4))) &3) <<2;
705 vadr=(vnapage[zz2][RefreshAddr&0x3ff]<<4)+vofs;
706 C = VRAMADR(vadr);
707
708 #include "fceline.h"
709
710 PPU_hook(vadr);
711
712 if((RefreshAddr&0x1f)==0x1f)
713 RefreshAddr^=0x41F;
714 else
715 RefreshAddr++;
716 }
4a1bf31b 717}
718
9115e7d2 719static void RefreshLine_normal(uint8 *P, uint32 vofs) // vofs is 0x107 max
4a1bf31b 720{
721 int8 X1;
9115e7d2 722 uint32 rfraddr = RefreshAddr;
723 uint8 *page = vnapage[(rfraddr>>10)&3];
724 uint32 cc2=0;
4a1bf31b 725
9115e7d2 726 if ((rfraddr&0xc)!=0)
727 cc2=*(uint32 *) (page + ((rfraddr&0x380)>>4) + ((rfraddr&0x10)>>2) + 0x3c0);
728
729 for (X1=33;X1;X1--,P+=8)
c62d2810 730 {
9115e7d2 731 uint8 cc,*C;
c62d2810 732 uint32 vadr;
733
9115e7d2 734 vadr=(page[rfraddr&0x3ff]<<4)+vofs;
c62d2810 735 C = VRAMADR(vadr);
9115e7d2 736 if ((rfraddr&0xc)==0)
737 cc2=*(uint32 *) (page + ((rfraddr&0x380)>>4) + ((rfraddr&0x10)>>2) + 0x3c0);
738 cc=((cc2 >> ((rfraddr&2) + ((rfraddr&0x40)>>4) + ((rfraddr&0xc)<<1))) & 3) << 2;
4a1bf31b 739
9115e7d2 740 #include "fceline.h"
15300263 741
9115e7d2 742 if((rfraddr&0x1f)==0x1f) {
743 rfraddr^=0x41F;
744 page = vnapage[(rfraddr>>10)&3];
745 } else
746 rfraddr++;
c62d2810 747 }
9115e7d2 748 RefreshAddr = rfraddr;
4a1bf31b 749}
c62d2810 750
4a1bf31b 751static void SetRefreshLine(void)
752{
753 if(MMC5Hack && geniestage!=1)
754 {
755 if(MMC5HackCHRMode==0 && (MMC5HackSPMode&0x80))
756 {
757 if (RefreshLine != RefreshLine_MMC5Hack1) printf("set refr RefreshLine_MMC5Hack1\n");
758 RefreshLine = RefreshLine_MMC5Hack1;
759 }
760 else if(MMC5HackCHRMode==1 && (MMC5HackSPMode&0x80))
761 {
762 if (RefreshLine != RefreshLine_MMC5Hack2) printf("set refr RefreshLine_MMC5Hack2\n");
763 RefreshLine = RefreshLine_MMC5Hack2;
764 }
765 else if(MMC5HackCHRMode==1)
766 {
767 if (RefreshLine != RefreshLine_MMC5Hack3) printf("set refr RefreshLine_MMC5Hack3\n");
768 RefreshLine = RefreshLine_MMC5Hack3;
769 }
770 else
771 {
772 if (RefreshLine != RefreshLine_MMC5Hack4) printf("set refr RefreshLine_MMC5Hack4\n");
773 RefreshLine = RefreshLine_MMC5Hack4;
774 }
775 } // End if(MMC5Hack)
776 else if(PPU_hook)
777 {
778 if (RefreshLine != RefreshLine_PPU_hook) printf("set refr RefreshLine_PPU_hook\n");
779 RefreshLine = RefreshLine_PPU_hook;
780 }
781 else
782 {
783 if (RefreshLine != RefreshLine_normal) printf("set refr RefreshLine_normal\n");
784 RefreshLine = RefreshLine_normal;
785 }
c62d2810 786}
787
937bf65b 788//static INLINE
789void Fixit2(void)
c62d2810 790{
791 if(ScreenON || SpriteON)
792 {
793 uint32 rad=RefreshAddr;
794 rad&=0xFBE0;
795 rad|=TempAddr&0x041f;
796 RefreshAddr=rad;
797 //PPU_hook(RefreshAddr,-1);
798 }
799}
800
937bf65b 801//static INLINE
802void Fixit1(void)
c62d2810 803{
804 if(ScreenON || SpriteON)
805 {
806 uint32 rad=RefreshAddr;
807
808 if((rad&0x7000)==0x7000)
809 {
810 rad^=0x7000;
811 if((rad&0x3E0)==0x3A0)
812 {
813 rad^=0x3A0;
814 rad^=0x800;
815 }
816 else
817 {
818 if((rad&0x3E0)==0x3e0)
819 rad^=0x3e0;
820 else rad+=0x20;
821 }
822 }
823 else
824 rad+=0x1000;
825 RefreshAddr=rad;
826 //PPU_hook(RefreshAddr,-1);
827 }
828}
829
937bf65b 830//#define NEW_TRY
831
c62d2810 832/* This is called at the beginning of all h-blanks on visible lines. */
937bf65b 833#ifndef NEW_TRY
c62d2810 834static void DoHBlank(void)
835{
836 if(ScreenON || SpriteON)
837 FetchSpriteData();
838 if(GameHBIRQHook && (ScreenON || SpriteON))
839 {
840 X6502_Run(12);
841 GameHBIRQHook();
842 X6502_Run(25-12);
843 Fixit2();
844 X6502_Run(85-25);
845 }
846 else
847 {
848 X6502_Run(25); // Tried 65, caused problems with Slalom(maybe others)
849 Fixit2();
850 X6502_Run(85-25);
851 }
852 //PPU_hook(0,-1);
853 //fprintf(stderr,"%3d: $%04x\n",scanline,RefreshAddr);
854}
937bf65b 855#endif
c62d2810 856
c62d2810 857
5232c20c 858// ============================//
859// end of new code
860// ===========================//
c62d2810 861
862void ResetMapping(void)
863{
864 int x;
865
866 SetReadHandler(0x0000,0xFFFF,ANull);
867 SetWriteHandler(0x0000,0xFFFF,BNull);
868
869 SetReadHandler(0,0x7FF,ARAML);
870 SetWriteHandler(0,0x7FF,BRAML);
871
872 SetReadHandler(0x800,0x1FFF,ARAMH); /* Part of a little */
873 SetWriteHandler(0x800,0x1FFF,BRAMH); /* hack for a small speed boost. */
874
875 for(x=0x2000;x<0x4000;x+=8)
876 {
877 ARead[x]=A200x;
878 BWrite[x]=B2000;
879 ARead[x+1]=A200x;
880 BWrite[x+1]=B2001;
881 ARead[x+2]=A2002;
882 BWrite[x+2]=B2002;
883 ARead[x+3]=A200x;
884 BWrite[x+3]=B2003;
885 ARead[x+4]=A200x;
886 BWrite[x+4]=B2004;
887 ARead[x+5]=A200x;
888 BWrite[x+5]=B2005;
889 ARead[x+6]=A200x;
890 BWrite[x+6]=B2006;
891 ARead[x+7]=A2007;
892 BWrite[x+7]=B2007;
893 }
894
895 BWrite[0x4014]=B4014;
896 SetNESSoundMap();
897 InitializeInput();
898}
899
900int GameLoaded=0;
901void CloseGame(void)
902{
903 if(GameLoaded)
904 {
905 if(FCEUGameInfo.type!=GIT_NSF)
906 FlushGameCheats();
907 #ifdef NETWORK
908 if(FSettings.NetworkPlay) KillNetplay();
15300263 909 #endif
c62d2810 910 GameInterface(GI_CLOSE);
911 CloseGenie();
912 GameLoaded=0;
913 }
914}
915
916void ResetGameLoaded(void)
917{
918 if(GameLoaded) CloseGame();
919 GameStateRestore=0;
920 PPU_hook=0;
921 GameHBIRQHook=0;
922 GameExpSound.Fill=0;
923 GameExpSound.RChange=0;
924 if(GameExpSound.Kill)
925 GameExpSound.Kill();
926 GameExpSound.Kill=0;
927 MapIRQHook=0;
928 MMC5Hack=0;
929 PAL&=1;
930 pale=0;
931
932 FCEUGameInfo.name=0;
933 FCEUGameInfo.type=GIT_CART;
934 FCEUGameInfo.vidsys=GIV_USER;
935 FCEUGameInfo.input[0]=FCEUGameInfo.input[1]=-1;
936 FCEUGameInfo.inputfc=-1;
937}
938
939FCEUGI *FCEUI_LoadGame(char *name)
940{
941 int fp;
942
943 Exit=1;
944 ResetGameLoaded();
945
946 fp=FCEU_fopen(name,"rb");
947 if(!fp)
948 {
949 FCEU_PrintError("Error opening \"%s\"!",name);
950 return 0;
951 }
952
953 GetFileBase(name);
954 if(iNESLoad(name,fp))
955 goto endlseq;
956 if(NSFLoad(fp))
957 goto endlseq;
958 if(FDSLoad(name,fp))
959 goto endlseq;
960 if(UNIFLoad(name,fp))
961 goto endlseq;
962
963 FCEU_PrintError("An error occurred while loading the file.");
964 FCEU_fclose(fp);
965 return 0;
966
967 endlseq:
968 FCEU_fclose(fp);
15300263 969 GameLoaded=1;
c62d2810 970
971 FCEU_ResetVidSys();
972 if(FCEUGameInfo.type!=GIT_NSF)
973 if(FSettings.GameGenie)
974 OpenGenie();
975
976 PowerNES();
977 #ifdef NETWORK
978 if(FSettings.NetworkPlay) InitNetplay();
979 #endif
980 SaveStateRefresh();
981 if(FCEUGameInfo.type!=GIT_NSF)
982 {
983 LoadGamePalette();
984 LoadGameCheats();
985 }
15300263 986
c62d2810 987 FCEU_ResetPalette();
988 Exit=0;
989 return(&FCEUGameInfo);
990}
991
992
993void FCEU_ResetVidSys(void)
994{
995 int w;
996
997 if(FCEUGameInfo.vidsys==GIV_NTSC)
998 w=0;
999 else if(FCEUGameInfo.vidsys==GIV_PAL)
1000 w=1;
1001 else
1002 w=FSettings.PAL;
1003
1004 if(w)
1005 {
1006 PAL=1;
1007 scanlines_per_frame=312;
1008 FSettings.FirstSLine=FSettings.UsrFirstSLine[1];
1009 FSettings.LastSLine=FSettings.UsrLastSLine[1];
1010 }
1011 else
1012 {
1013 PAL=0;
1014 scanlines_per_frame=262;
1015 FSettings.FirstSLine=FSettings.UsrFirstSLine[0];
1016 FSettings.LastSLine=FSettings.UsrLastSLine[0];
1017 }
15300263 1018 printf("PAL = %i\n", PAL);
c62d2810 1019 SetSoundVariables();
1020}
1021
1022int FCEUI_Initialize(void)
1023{
1024 if(!InitVirtualVideo())
1025 return 0;
1026 memset(&FSettings,0,sizeof(FSettings));
1027 FSettings.UsrFirstSLine[0]=8;
1028 FSettings.UsrFirstSLine[1]=0;
1029 FSettings.UsrLastSLine[0]=FSettings.UsrLastSLine[1]=239;
5232c20c 1030 FSettings.SoundVolume=65535; // 100%
c62d2810 1031 return 1;
1032}
1033
1034#define harko 0xe //0x9
1035static INLINE void Thingo(void)
1036{
1037 Loop6502();
937bf65b 1038#ifndef NEW_TRY
c62d2810 1039
1040 if(tosprite>=256)
15300263 1041 {
c62d2810 1042 X6502_Run(256-harko);
1043 Fixit1();
1044 X6502_Run(harko);
1045 }
1046 else
1047 {
1048 if(tosprite<=240)
1049 {
1050 X6502_Run(tosprite);
1051 PPU[2]|=0x40;
1052 X6502_Run(256-tosprite-harko);
1053 Fixit1();
1054 X6502_Run(harko);
1055 }
1056 else
1057 {
1058 X6502_Run(256-harko);
1059 Fixit1();
1060 X6502_Run(tosprite-(256-harko));
1061 PPU[2]|=0x40;
1062 X6502_Run(256-tosprite);
1063 }
1064 tosprite=256;
1065 }
1066 DoHBlank();
937bf65b 1067#else
1068 X6502_Run_scanline();
1069#endif
c62d2810 1070}
1071#undef harko
1072
1073void EmLoop(void)
1074{
1075 for(;;)
1076 {
937bf65b 1077 //extern int asdc;
1078 //printf("asdc: %i\n", asdc);
1079 //asdc=0;
c62d2810 1080 ApplyPeriodicCheats();
1081 X6502_Run(256+85);
1082
1083 PPU[2]|=0x80;
1084 PPU[3]=PPUSPL=0; /* Not sure if this is correct. According
1085 to Matt Conte and my own tests, it is. Timing is probably
1086 off, though. NOTE: Not having this here
1087 breaks a Super Donkey Kong game. */
1088
1089 X6502_Run(12); /* I need to figure out the true nature and length
15300263 1090 of this delay.
c62d2810 1091 */
1092 if(FCEUGameInfo.type==GIT_NSF)
1093 TriggerNMINSF();
1094 else if(VBlankON)
1095 TriggerNMI();
1096
15300263 1097 X6502_Run((scanlines_per_frame-242)*(256+85)-12);
c62d2810 1098
1099 PPU_status&=0x1f;
1100
1101 X6502_Run(256);
1102 {
1103 static int kook=0;
1104 if(ScreenON || SpriteON)
1105 if(GameHBIRQHook)
1106 GameHBIRQHook();
1107
1108 X6502_Run(85-kook);
1109 kook=(kook+1)&1;
1110 }
1111
1112 if(ScreenON || SpriteON)
1113 {
1114 RefreshAddr=TempAddr;
1115 if(PPU_hook) PPU_hook(RefreshAddr&0x3fff);
1116 }
1117 if(FCEUGameInfo.type==GIT_NSF)
937bf65b 1118 {
c62d2810 1119 X6502_Run((256+85)*240);
937bf65b 1120 }
c62d2810 1121 else
1122 {
1123 int x,max,maxref;
1124
1125 deemp=PPU[1]>>5;
4a1bf31b 1126 SetRefreshLine();
c62d2810 1127 for(scanline=0;scanline<240;scanline++)
1128 {
1129 deempcnt[deemp]++;
1130 Thingo();
1131 }
1132 for(x=1,max=0,maxref=0;x<7;x++)
1133 {
1134 if(deempcnt[x]>max)
1135 {
1136 max=deempcnt[x];
1137 maxref=x;
1138 }
1139 deempcnt[x]=0;
1140 }
1141 //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);
1142 //memset(deempcnt,0,sizeof(deempcnt));
1143 SetNESDeemph(maxref,0);
1144 }
1145
1146 {
1147 int ssize;
1148
1149 ssize=FlushEmulateSound();
1150
1151 #ifdef FRAMESKIP
1152 if(FSkip)
1153 {
1154 FCEU_PutImageDummy();
1155 FSkip--;
5232c20c 1156 FCEUD_Update(0,WaveFinalMono,ssize);
c62d2810 1157 }
1158 else
1159 #endif
1160 {
1161 FCEU_PutImage();
5232c20c 1162 FCEUD_Update(XBuf+8,WaveFinalMono,ssize);
c62d2810 1163 }
1164 UpdateInput();
1165 }
1166
1167 if(Exit)
1168 {
1169 CloseGame();
1170 break;
1171 }
1172
1173 }
1174}
1175
1176#ifdef FPS
1177#include <sys/time.h>
1178uint64 frcount;
1179#endif
1180void FCEUI_Emulate(void)
1181{
1182 #ifdef FPS
1183 uint64 starttime,end;
1184 struct timeval tv;
1185 frcount=0;
1186 gettimeofday(&tv,0);
1187 starttime=((uint64)tv.tv_sec*1000000)+tv.tv_usec;
1188 #endif
1189 EmLoop();
1190
1191 #ifdef FPS
1192 // Probably won't work well on Windows port; for
1193 // debugging/speed testing.
1194 {
1195 uint64 w;
1196 int i,frac;
1197 gettimeofday(&tv,0);
1198 end=((uint64)tv.tv_sec*1000000)+tv.tv_usec;
1199 w=frcount*10000000000LL/(end-starttime);
1200 i=w/10000;
1201 frac=w-i*10000;
1202 printf("Average FPS: %d.%04d\n",i,frac);
1203 }
1204 #endif
1205
1206}
1207
1208void FCEUI_CloseGame(void)
1209{
1210 Exit=1;
1211}
1212
1213static void ResetPPU(void)
1214{
1215 VRAMBuffer=PPU[0]=PPU[1]=PPU[2]=PPU[3]=0;
1216 PPUSPL=0;
1217 PPUGenLatch=0;
1218 RefreshAddr=TempAddr=0;
1219 vtoggle = 0;
1220}
1221
1222static void PowerPPU(void)
1223{
1224 memset(NTARAM,0x00,0x800);
1225 memset(PALRAM,0x00,0x20);
1226 memset(SPRAM,0x00,0x100);
1227 ResetPPU();
1228}
1229
1230void ResetNES(void)
1231{
1232 if(!GameLoaded || (FCEUGameInfo.type==GIT_NSF)) return;
1233 GameInterface(GI_RESETM2);
1234 ResetSound();
1235 ResetPPU();
1236 X6502_Reset();
1237}
1238
15300263 1239void PowerNES(void)
c62d2810 1240{
1241 if(!GameLoaded) return;
1242
1243 FCEU_CheatResetRAM();
1244 FCEU_CheatAddRAM(2,0,RAM);
1245
1246 GeniePower();
1247
1248 memset(RAM,0x00,0x800);
1249 ResetMapping();
1250 GameInterface(GI_POWER);
1251 PowerSound();
1252 PowerPPU();
1253 timestampbase=0;
1254 X6502_Power();
1255}
1256