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