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