#include "types.h"
#include "x6502.h"
#include "fce.h"
+#include "fceu098.h"
#include "sound.h"
-#include "svga.h"
+#include "svga.h"
#include "netplay.h"
#include "general.h"
#include "endian.h"
#include "fds.h"
#include "ines.h"
#include "unif.h"
+#include "vsuni.h"
#include "cheat.h"
#include "state.h"
#include "file.h"
#include "crc32.h"
#include "ppu.h"
+#include "ppu098.h"
#include "palette.h"
#include "movie.h"
#include "dprintf.h"
-#ifdef GP2X
-#include "drivers/gp2x/asmutils.h"
+#ifdef __arm__
+#include "drivers/arm/asmutils.h"
#endif
#define Pal (PALRAM)
uint64 timestampbase=0;
-static int ppudead=1;
-static int kook=0;
+int ppudead=1;
+int kook=0;
int MMC5Hack;
uint32 MMC5HackVROMMask;
static uint8 deemp=0;
static int deempcnt[8];
-int tosprite=256;
-
FCEUGI FCEUGameInfo;
-void (*GameInterface)(int h);
+void (*GameInterface)(int h, void *param);
void FP_FASTAPASS(1) (*PPU_hook)(uint32 A);
static int RWWrap=0;
#ifdef ASM_6502
+#ifdef DEBUG_ASM_6502
+extern uint8 nes_internal_ram[0x800];
+#else
static void asmcpu_update(int32 cycles)
{
// some code from x6502.c
}
}
}
+#endif
void asmcpu_unpack(void)
{
X6502_Rebase_a();
nes_registers[4] = X.S << 24;
nes_registers[4]|= X.IRQlow << 8;
+ if (MapIRQHook)
+ nes_registers[4] |= 1<<16; // MapIRQHook set bit
nes_registers[7] = (uint32)X.count << 16;
// NVUB DIZC
nes_registers[4]|= X.P & 0x5d;
nes_registers[5] = X.P << 24; // N
if (!(X.P&0x02)) nes_registers[5] |= 1; // Z
+
+#ifdef DEBUG_ASM_6502
+ memcpy(nes_internal_ram, RAM, 0x800);
+#endif
}
void asmcpu_pack(void)
DECLFR(ANull)
{
+ //printf("open [%04x] %02x @ %04x (%04x)\n", A, X.DB, X.PC, X.PC&0x7ff);
return(X.DB);
}
static int linestartts;
static int tofix=0;
-static void ResetRL(void)
+static uint8 *Plinef;
+
+extern uint8 sprlinebuf[256+8];
+extern int32 sphitx;
+extern uint8 sphitdata;
+
+extern int spork; /* spork the world. Any sprites on this line?
+ Then this will be set to 1. Needed for zapper
+ emulation and *gasp* sprite emulation.
+ */
+
+static void ResetRL(uint8 *target)
{
+ if(InputScanlineHook)
+ InputScanlineHook(0,0,0,0);
+ Plinef=target;
linestartts=timestamp*48+X6502_GetCycleCount();
tofix=1;
}
static INLINE void Fixit1(void);
-static void TryFixit1(void)
+/* faking FCEUPPU_LineUpdate() from later versions of the emu */
+static void FakedLineUpdate(void)
{
#define TOFIXNUM (272-0x4)
int lastpixel;
- if (scanline < 240 && tofix)
+ if (scanline >= 240) return;
+
+ if (tofix || sphitx != 0x100)
{
lastpixel = (timestamp*48-linestartts)>>4;
if (PAL) lastpixel += lastpixel>>4;
-
//printf("lastpixel: %i\n", lastpixel);
+ }
- if(lastpixel>=TOFIXNUM)
+ if (tofix && lastpixel>=TOFIXNUM)
+ {
+ Fixit1();
+ tofix=0;
+ }
+
+ // CheckSpriteHit()
+ if(sphitx!=0x100)
+ {
+ int l=lastpixel-16;
+ int x;
+
+ for(x=sphitx;x<(sphitx+8) && x<l;x++)
{
- Fixit1();
- tofix=0;
+ if((sphitdata&(0x80>>(x-sphitx))) && !(Plinef[x]&64))
+ {
+ PPU_status|=0x40;
+ sphitx=0x100;
+ break;
+ }
}
}
}
/* merged */
uint8 ret;
- TryFixit1();
+ FakedLineUpdate();
ret = PPU_status;
ret|=PPUGenLatch&0x1F;
vtoggle=0;
static DECLFR(A200x)
{
/* merged */
- TryFixit1();
+ FakedLineUpdate();
return PPUGenLatch;
}
uint8 ret;
uint32 tmp=RefreshAddr&0x3FFF;
- TryFixit1();
+ FakedLineUpdate();
ret=VRAMBuffer;
static DECLFW(B2000)
{
/* NMI2? */
- TryFixit1();
+ FakedLineUpdate();
PPUGenLatch=V;
PPU[0]=V;
TempAddr&=0xF3FF;
static DECLFW(B2001)
{
/* merged */
- TryFixit1();
+ FakedLineUpdate();
PPUGenLatch=V;
PPU[1]=V;
if(V&0xE0)
{
/* merged */
uint32 tmp=TempAddr;
- TryFixit1();
+ FakedLineUpdate();
PPUGenLatch=V;
if (!vtoggle)
{
static DECLFW(B2006)
{
/* merged */
- TryFixit1();
+ FakedLineUpdate();
PPUGenLatch=V;
if(!vtoggle)
}
#ifdef FRAMESKIP
-int FSkip_setting=-1; // auto
int FSkip=0;
void FCEUI_FrameSkip(int x)
{
#endif
/* This is called at the beginning of each visible scanline */
-static void Loop6502(void)
+static void LineUpdate(uint8 *target)
{
uint32 tem;
- int x;
- uint8 *target=XBuf+scanline*320+32;
+ int y;
- if(ScreenON || SpriteON)
- {
- /* PRefreshLine() will not get called on skipped frames. This
- could cause a problem, but the solution would be rather complex,
- due to the current sprite 0 hit code.
+ /* PRefreshLine() will not get called on skipped frames. This
+ * could cause a problem, but the solution would be rather complex,
+ * due to the current sprite 0 hit code.
*/
- #ifdef FRAMESKIP
- if(!FSkip)
- {
- #endif
- if(ScreenON)
- {
- if(scanline>=FSettings.FirstSLine && scanline<=FSettings.LastSLine)
- BGRender(target);
- else
- {
- if(PPU_hook)
- PRefreshLine();
- }
- }
- else
- {
- tem=Pal[0]|(Pal[0]<<8)|(Pal[0]<<16)|(Pal[0]<<24);
- tem|=0x40404040;
- FCEU_dwmemset(target,tem,264);
- }
- #ifdef FRAMESKIP
- }
- #endif
- if (SpriteON && scanline)
- RefreshSprite(target);
- #ifdef FRAMESKIP
- if(!FSkip)
- {
- #endif
- if(PPU[1]&0x01)
- {
- for(x=63;x>=0;x--)
- ((uint32 *)target)[x]=((uint32*)target)[x]&0xF0F0F0F0;
- }
-#ifdef GP2X
- if((PPU[1]>>5)==0x7) block_or(target, 256, 0xc0);
- else if(PPU[1]&0xE0) block_andor(target, 256, 0x3f, 0x40);
- else block_andor(target, 256, 0x3f, 0x80);
-#else
- if((PPU[1]>>5)==0x7)
- for(x=63;x>=0;x--)
- ((uint32 *)target)[x]=(((uint32*)target)[x])|0xc0c0c0c0;
- else if(PPU[1]&0xE0)
- for(x=63;x>=0;x--)
- ((uint32 *)target)[x]=(((uint32*)target)[x]&0x3f3f3f3f)|0x40404040;
- else
- for(x=63;x>=0;x--)
- ((uint32 *)target)[x]=(((uint32*)target)[x]&0x3f3f3f3f)|0x80808080;
-#endif
- // black borders
- ((uint32 *)target)[-2]=((uint32 *)target)[-1]=0;
- ((uint32 *)target)[64]=((uint32 *)target)[65]=0;
- #ifdef FRAMESKIP
- }
- #endif
+ if(FSkip)
+ {
+ y=(int)SPRAM[0] + 1;
+ if(scanline==y && SpriteON) PPU_status|=0x40; // hack
+ return;
+ }
+
+ if(!ScreenON)
+ {
+ tem=Pal[0]|0x40;
+ tem|=tem << 8;
+ tem|=tem << 16;
+ FCEU_dwmemset(target,tem,256);
+ }
+ else if(scanline < FSettings.FirstSLine || scanline > FSettings.LastSLine)
+ {
+ if(PPU_hook)
+ PRefreshLine();
+ y=(int)SPRAM[0] + 1;
+ if(scanline==y && SpriteON) PPU_status|=0x40;
}
else
{
- tem=Pal[0]|(Pal[0]<<8)|(Pal[0]<<16)|(Pal[0]<<24);
- FCEU_dwmemset(target,tem,256);
+ BGRender(target);
}
+
if(InputScanlineHook)
- InputScanlineHook(target, scanline);
+ InputScanlineHook(target,spork?sprlinebuf:0,linestartts,256);
+}
+
+
+static void LineUpdateEnd(uint8 *target)
+{
+#ifdef __arm__
+ if(ScreenON || SpriteON) // Yes, very el-cheapo.
+ {
+ if(PPU[1]&0x01)
+ block_and(target, 256, 0x30);
+ }
+ if((PPU[1]>>5)==0x7)
+ block_or(target, 256, 0xc0);
+ else if(PPU[1]&0xE0)
+ block_or(target, 256, 0x40);
+ else
+ block_andor(target, 256, 0x3f, 0x80);
+#else
+ int x;
+
+ if(ScreenON || SpriteON) // Yes, very el-cheapo.
+ {
+ if(PPU[1]&0x01)
+ {
+ for(x=63;x>=0;x--)
+ *(uint32 *)&target[x<<2]=(*(uint32*)&target[x<<2])&0x30303030;
+ }
+ }
+ if((PPU[1]>>5)==0x7)
+ {
+ for(x=63;x>=0;x--)
+ *(uint32 *)&target[x<<2]=((*(uint32*)&target[x<<2])&0x3f3f3f3f)|0xc0c0c0c0;
+ }
+ else if(PPU[1]&0xE0)
+ for(x=63;x>=0;x--)
+ *(uint32 *)&target[x<<2]=(*(uint32*)&target[x<<2])|0x40404040;
+ else
+ for(x=63;x>=0;x--)
+ *(uint32 *)&target[x<<2]=((*(uint32*)&target[x<<2])&0x3f3f3f3f)|0x80808080;
+#endif
+
+ // black borders
+ ((uint32 *)target)[-2]=((uint32 *)target)[-1]=0;
+ ((uint32 *)target)[64]=((uint32 *)target)[65]=0;
}
#define PAL(c) ((c)+cc)
static void PRefreshLine(void)
{
- uint32 vofs;
- uint8 X1;
+ uint32 vofs;
+ int X1;
+ vofs=((PPU[0]&0x10)<<8) | ((RefreshAddr>>12)&7);
+ void (*PPU_hook_)(uint32 A) = PPU_hook;
- vofs = 0;
- if (BGAdrHI) vofs = 0x1000;
- vofs+=(RefreshAddr>>12)&7;
+ for(X1=33;X1;X1--)
+ {
+ uint32 zz2;
+ uint32 vadr;
+
+ zz2=(RefreshAddr>>10)&3;
+ PPU_hook_(0x2000|(RefreshAddr&0xFFF));
+
+ vadr=(vnapage[zz2][RefreshAddr&0x3ff]<<4)+vofs;
+
+ PPU_hook_(vadr);
- for(X1=33;X1;X1--)
- {
- register uint8 no;
- register uint8 zz2;
- zz2=(uint8)((RefreshAddr>>10)&3);
- PPU_hook(0x2000|(RefreshAddr&0xFFF));
- no = vnapage[zz2][(RefreshAddr&0x3ff)];
- PPU_hook((no<<4)+vofs);
if((RefreshAddr&0x1f)==0x1f)
RefreshAddr^=0x41F;
else
RefreshAddr++;
- }
+ }
}
/* This high-level graphics MMC5 emulation code was written
static void RefreshLine_PPU_hook(uint8 *P, uint32 vofs)
{
int8 X1;
+ void (*PPU_hook_)(uint32 A) = PPU_hook;
+ uint32 rfraddr = RefreshAddr;
+ uint8 *page = vnapage[(rfraddr>>10)&3];
for(X1=33;X1;X1--,P+=8)
{
uint8 *C;
- uint8 cc,zz,zz2;
+ uint8 cc,zz;
uint32 vadr;
- zz=RefreshAddr&0x1F;
- zz2=(RefreshAddr>>10)&3;
- PPU_hook(0x2000|(RefreshAddr&0xFFF));
- cc=vnapage[zz2][0x3c0+(zz>>2)+((RefreshAddr&0x380)>>4)];
- cc=((cc >> ((zz&2) + ((RefreshAddr&0x40)>>4))) &3) <<2;
- vadr=(vnapage[zz2][RefreshAddr&0x3ff]<<4)+vofs;
+ zz=rfraddr&0x1F;
+ PPU_hook_(0x2000|(rfraddr&0xFFF));
+ cc=page[0x3c0+(zz>>2)+((rfraddr&0x380)>>4)];
+ cc=((cc >> ((zz&2) + ((rfraddr&0x40)>>4))) &3) <<2;
+ vadr=(page[rfraddr&0x3ff]<<4)+vofs;
C = VRAMADR(vadr);
#include "fceline.h"
- PPU_hook(vadr);
+ PPU_hook_(vadr);
- if((RefreshAddr&0x1f)==0x1f)
- RefreshAddr^=0x41F;
- else
- RefreshAddr++;
+ if((rfraddr&0x1f)==0x1f) {
+ rfraddr^=0x41F;
+ page = vnapage[(rfraddr>>10)&3];
+ } else
+ rfraddr++;
}
+ RefreshAddr = rfraddr;
}
static void RefreshLine_normal(uint8 *P, uint32 vofs) // vofs is 0x107 max
}
-/* This is called at the beginning of all h-blanks on visible lines. */
-static void DoHBlank(void)
-{
- if(ScreenON || SpriteON)
- FetchSpriteData();
- if(GameHBIRQHook && (ScreenON || SpriteON) && ((PPU[0]&0x38)!=0x18))
- {
- X6502_Run(6);
- Fixit2();
- X6502_Run(4);
- GameHBIRQHook();
- X6502_Run(85-16-10);
- }
- else
- {
- X6502_Run(6); // Tried 65, caused problems with Slalom(maybe others)
- Fixit2();
- X6502_Run(85-6-16);
- }
- if(GameHBIRQHook2 && (ScreenON || SpriteON))
- GameHBIRQHook2();
- //PPU_hook(0,-1);
- //fprintf(stderr,"%3d: $%04x\n",scanline,RefreshAddr);
- scanline++;
- if (scanline<240)
- ResetRL();
- X6502_Run(16);
-}
-
-
// ============================//
// end of new code
// ===========================//
int GameLoaded=0;
void CloseGame(void)
{
+ FCEUI_StopMovie();
if(GameLoaded)
{
if(FCEUGameInfo.type!=GIT_NSF)
#ifdef NETWORK
if(FSettings.NetworkPlay) KillNetplay();
#endif
- GameInterface(GI_CLOSE);
+ GameInterface(GI_CLOSE, 0);
CloseGenie();
GameLoaded=0;
}
FCEUGameInfo.vidsys=GIV_USER;
FCEUGameInfo.input[0]=FCEUGameInfo.input[1]=-1;
FCEUGameInfo.inputfc=-1;
+
+ FCEUGameInfo.soundchan=0;
+ FCEUGameInfo.soundrate=0;
+ FCEUGameInfo.cspecial=0;
}
char lastLoadedGameName [2048];
+int LoadGameLastError = 0;
int UNIFLoad(const char *name, int fp);
int iNESLoad(const char *name, int fp);
int FDSLoad(const char *name, int fp);
FCEUGI *FCEUI_LoadGame(char *name)
{
char name2[512];
- int have_movie = 0;
+ int have_movie = 0, have_ips = 0;
int fp;
- Exit=1;
+ //Exit=1;
+ LoadGameLastError = 0;
ResetGameLoaded();
strncpy(name2, name, sizeof(name2));
if(!fp)
{
FCEU_PrintError("Error opening \"%s\"!",name);
+ LoadGameLastError = 1;
return 0;
}
{
char *p = name2 + strlen(name2) - 4;
- if (strcmp(p, ".fcm") == 0)
+ if (strcasecmp(p, ".fcm") == 0) printf("movie detected\n"), have_movie = 1;
+ if (strcasecmp(p, ".ips") == 0) printf("ips detected\n"), have_ips = 1;
+ if (have_movie || have_ips)
{
// movie detected
- printf("movie detected\n");
FCEU_fclose(fp);
*p = 0;
fp=FCEU_fopen(name2,"rb");
+ if (!fp && p - name2 > 2)
+ {
+ for (p--; p > name2 && *p != '.'; p--);
+ *p = 0;
+ fp=FCEU_fopen(name2,"rb");
+ }
if (!fp) {
- printf("no ROM for movie\n");
+ printf("no ROM for ips/movie\n");
+ LoadGameLastError = 2;
return 0;
}
- have_movie = 1;
}
}
- strcpy(lastLoadedGameName, name2);
+ // do IPS patch
+ if (have_ips)
+ {
+ FCEU_fclose(fp);
+ FILE *ips = fopen(name, "rb");
+ if (!ips) return 0;
+ fp=FCEU_fopen_forcemem(name2);
+ if (!fp) { fclose(ips); return 0; }
+ ApplyIPS(ips, fp); // closes ips
+ }
GetFileBase(name2);
if(iNESLoad(name2,fp))
FCEU_PrintError("An error occurred while loading the file.");
FCEU_fclose(fp);
+ // format handlers may set LoadGameLastError themselves.
+ if (LoadGameLastError == 0) LoadGameLastError = 3;
return 0;
endlseq:
if (have_movie)
FCEUI_LoadMovie(name, 1);
+
+ strcpy(lastLoadedGameName, name2);
+
return(&FCEUGameInfo);
}
FSettings.UsrFirstSLine[1]=0;
FSettings.UsrLastSLine[0]=FSettings.UsrLastSLine[1]=239;
FSettings.SoundVolume=100;
+
+ FCEUI_Initialize098();
+ FCEUI_SetEmuMode(0);
+
return 1;
}
+void FCEUI_Kill(void)
+{
+ FCEU_KillGenie();
+}
+
+static void EmLoop(void);
+
+int use098code = 0;
+void (*ResetNES)(void) = 0;
+void (*FCEUI_Emulate)(void) = 0;
+
+void FCEUI_SetEmuMode(int is_new)
+{
+ use098code = is_new;
+ if (is_new)
+ {
+ ResetNES=ResetNES098;
+ FCEUI_Emulate=FCEUI_Emulate098;
+ }
+ else
+ {
+ ResetNES=ResetNES081;
+ FCEUI_Emulate=EmLoop;
+ }
+}
+
void MMC5_hb(int); /* Ugh ugh ugh. */
-static INLINE void Thingo(void)
+static void DoLine(void)
{
- Loop6502();
+ uint8 *target=XBuf+scanline*320+32;
+
+ LineUpdate(target);
if(MMC5Hack && (ScreenON || SpriteON)) MMC5_hb(scanline);
- // check: Battletoads & Double Dragon
- if(tosprite>=256)
+ X6502_Run(256);
+
+ // check: Battletoads & Double Dragon, Addams Family
+ // sky glitches in SMB1 if done wrong
+ FakedLineUpdate();
+
+#ifdef FRAMESKIP
+ if(!FSkip)
+#endif
+ if(scanline>=FSettings.FirstSLine && scanline<=FSettings.LastSLine)
+ {
+ if(SpriteON && spork)
+ CopySprites(target);
+
+ LineUpdateEnd(target);
+ }
+ sphitx=0x100;
+
+ if(ScreenON || SpriteON)
+ FetchSpriteData();
+
+ // DoHBlank();
+ if(GameHBIRQHook && (ScreenON || SpriteON) && ((PPU[0]&0x38)!=0x18))
{
- X6502_Run(256);
+ X6502_Run(6);
+ Fixit2();
+ X6502_Run(4);
+ GameHBIRQHook();
+ X6502_Run(85-10-16);
}
else
{
- // sky glitches in SMB1 if done wrong
- X6502_Run(tosprite);
- PPU[2]|=0x40;
- X6502_Run(256-tosprite);
- tosprite = 256;
+ X6502_Run(6); // Tried 65, caused problems with Slalom(maybe others)
+ Fixit2();
+ X6502_Run(85-6-16);
}
- TryFixit1();
- DoHBlank();
+
+ if(SpriteON)
+ RefreshSprites();
+ if(GameHBIRQHook2 && (ScreenON || SpriteON))
+ GameHBIRQHook2();
+ scanline++;
+ if (scanline<240)
+ ResetRL(XBuf+scanline*320+32);
+ X6502_Run(16);
}
-void EmLoop(void)
+
+static void EmLoop(void)
{
for(;;)
{
if(ppudead) /* Needed for Knight Rider, possibly others. */
{
//memset(XBuf, 0, 320*240);
- X6502_Run(scanlines_per_frame*(256+85));
+ //X6502_Run(scanlines_per_frame*(256+85));
+ int lines;
+ for (lines=scanlines_per_frame;lines;lines--)
+ X6502_Run(256+85);
ppudead--;
goto update;
}
RefreshAddr=TempAddr;
if(PPU_hook) PPU_hook(RefreshAddr&0x3fff);
}
- ResetRL();
+ spork=0;
+ ResetRL(XBuf+32);
X6502_Run(16-kook);
kook ^= 1;
for(scanline=0;scanline<240;) // scanline is incremented in DoLine. Evil. :/
{
deempcnt[deemp]++;
- Thingo();
+ DoLine();
}
if(MMC5Hack && (ScreenON || SpriteON)) MMC5_hb(scanline);
for(x=1,max=0,maxref=0;x<7;x++)
}
update:
+ if(Exit)
+ {
+ //CloseGame();
+ break;
+ }
+
{
int ssize;
}
}
- if(Exit)
- {
- CloseGame();
- break;
- }
-
} // for
}
-#ifdef FPS
-#include <sys/time.h>
-uint64 frcount;
-#endif
-void FCEUI_Emulate(void)
-{
- #ifdef FPS
- uint64 starttime,end;
- struct timeval tv;
- frcount=0;
- gettimeofday(&tv,0);
- starttime=((uint64)tv.tv_sec*1000000)+tv.tv_usec;
- #endif
- EmLoop();
-
- #ifdef FPS
- // Probably won't work well on Windows port; for
- // debugging/speed testing.
- {
- uint64 w;
- int i,frac;
- gettimeofday(&tv,0);
- end=((uint64)tv.tv_sec*1000000)+tv.tv_usec;
- w=frcount*10000000000LL/(end-starttime);
- i=w/10000;
- frac=w-i*10000;
- printf("Average FPS: %d.%04d\n",i,frac);
- }
- #endif
-
-}
-
void FCEUI_CloseGame(void)
{
Exit=1;
ResetPPU();
}
-void ResetNES(void)
+void ResetNES081(void)
{
if(!GameLoaded) return;
- GameInterface(GI_RESETM2);
+ GameInterface(GI_RESETM2, 0);
ResetSound();
ResetPPU();
X6502_Reset();
}
+#ifndef DEBUG_ASM_6502
static void FCEU_MemoryRand(uint8 *ptr, uint32 size)
{
int x=0;
ptr++;
}
}
+#endif
void PowerNES(void)
{
FCEU_MemoryRand(RAM,0x800);
#else
memset(RAM,0x00,0x800);
+ memset(nes_internal_ram,0x00,0x800);
#endif
ResetMapping();
- GameInterface(GI_POWER);
PowerSound();
PowerPPU();
- timestampbase=0;
+
+ if (use098code)
+ FCEUPPU_Power();
+
+ /* Have the external game hardware "powered" after the internal NES stuff.
+ Needed for the NSF code and VS System code.
+ */
+ GameInterface(GI_POWER, 0);
+ if(FCEUGameInfo.type==GIT_VSUNI)
+ FCEU_VSUniPower();
#ifdef ASM_6502
if (geniestage)
GenieSetPages(0);
#endif
+ timestampbase=0;
X6502_Power();
+ FCEU_PowerCheats();
}