📄 gb.c
字号:
for(I=0;I<256;I++) RAMMap[I]=ROMMap[I]=NULL;
if(Verbose) printf("Allocating 64kB for address space...");
if(!(RAM=malloc(0x10000)))
{ if(Verbose) puts("FAILED");return(0); }
memset(RAM,NORAM,0x10000);
for(I=0;I<8;I++) Page[I]=RAM+I*0x2000;
if(Verbose) printf("OK\nOpening %s...",CartName);
if(!(F=fopen(CartName,"rb")))
{ if(Verbose) puts("FAILED");return(0); }
if(Verbose) printf("reading...");
if(fread(RAM,1,0x4000,F)!=0x4000)
{ if(Verbose) puts("FAILED");return(0); }
ROMMap[0]=RAM;
ROMBanks=2<<RAM[0x0148];
RAMBanks=rambanks[RAM[0x0149]&3];
Checksum=((word)RAM[0x014E]<<8)+RAM[0x014F];
MBCType=RAM[0x0147]>3;
P=NULL;
if((RAM[0x0147]==4)||(RAM[0x0147]>6)) P="Unknown ROM type";
if(P)
{
printf("\nError loading cartridge: %s\n",P);
fclose(F);return(0);
}
if(Verbose)
{
strncpy(S,RAM+0x0134,16);S[16]='\0';
printf("OK\n Name: %s\n",S);
printf(" Type: %s\n",CartTypes[RAM[0x0147]]);
printf(" ROM Size: %dx16kB\n",ROMBanks);
J=(RAM[0x0149]&0x03)*2;J=J? (1<<(J-1)):0;
printf(" RAM Size: %dkB\n",J);
J=((word)RAM[0x014B]<<8)+RAM[0x014A];
for(I=0,P=NULL;!P&&Companies[I].Name;I++)
if(J==Companies[I].Code) P=Companies[I].Name;
printf(" Manufacturer ID: %Xh",J);
printf(" [%s]\n",P? P:"?");
printf(" Version Number: %Xh\n",RAM[0x014C]);
printf(" Complement Check: %Xh\n",RAM[0x014D]);
printf(" Checksum: %Xh\n",Checksum);
J=((word)RAM[0x0103]<<8)+RAM[0x0102];
printf(" Start Address: %Xh\n",J);
}
Checksum+=RAM[0x014E]+RAM[0x014F];
for(I=0;I<0x4000;I++) Checksum-=RAM[I];
if(Verbose) printf("Loading %dx16kB ROM banks:\n.",ROMBanks);
for(I=1;I<ROMBanks;I++)
if(ROMMap[I]=malloc(0x4000))
if(fread(ROMMap[I],1,0x4000,F)==0x4000)
{
for(J=0;J<0x4000;J++) Checksum-=ROMMap[I][J];
if(Verbose) putchar('.');
}
else { if(Verbose) puts("READ FAILED");break; }
else { if(Verbose) puts("MALLOC FAILED");break; }
fclose(F);if(I<ROMBanks) return(0);
if(CheckCRC&&(Checksum&0xFFFF))
{ puts("\nError loading cartridge: Checksum is wrong");return(0); }
if(Verbose) puts("OK");
if(RAMBanks&&!MBCType)
{
if(Verbose) printf("Allocating %dx8kB RAM banks...",RAMBanks);
for(I=0;I<RAMBanks;I++)
if(RAMMap[I]=malloc(0x2000))
memset(RAMMap[I],0,0x2000);
else
{ if(Verbose) puts("FAILED");return(0); }
if(Verbose) puts("OK");
}
if((RAM[0x0147]==3)||(RAM[0x0147]==6))
{
if (SaveName)
{
if(Verbose) printf("Opening %s...",SaveName);
if(F=fopen(SaveName,"rb"))
{
if(Verbose) printf("reading...");
J=RAM[0x0147]==3? 0x2000:0x0200;
J=(fread(RAMMap[0]? RAMMap[0]:Page[5],1,J,F)==J);
if(Verbose) puts(J? "OK":"FAILED");
fclose(F);
}
else if(Verbose) puts("FAILED");
}
}
else SaveName=NULL;
if(CheatCount>0)
{
if(Verbose) puts("Patching cheats into the ROM code:");
for(J=0;J<CheatCount;J++)
{
A=Cheats[J].Address;
if(Verbose)
printf(" at %Xh: %Xh -> %Xh\n",A,Cheats[J].Orig,Cheats[J].Value);
if(A<0x4000)
{ if(ROMMap[0][A]==Cheats[J].Orig) ROMMap[0][A]=Cheats[J].Value; }
else
for(I=0,A-=0x4000;I<ROMBanks;I++)
if(ROMMap[I][A]==Cheats[J].Orig) ROMMap[I][A]=Cheats[J].Value;
}
}
IPeriod=VPeriod*256/(154*11);
if(SndName)
{
if(Verbose) printf("Logging soundtrack to %s...",SndName);
SndStream=fopen(SndName,"wb");
if(Verbose) puts(SndStream? "OK":"FAILED");
if(SndStream)
{
fprintf(SndStream,"GameBoy Soundtrack File 1.1\032");
I=154*11*IFreq;
fputc (I&255,SndStream);
fputc (I>>8,SndStream);
fputc (I>>16,SndStream);
fputc (I>>24,SndStream);
for (I=4;I<64;++I)
fputc (0,SndStream);
}
}
if(ROMBanks<3) ROMMask=0;
else { for(I=1;I<ROMBanks;I<<=1);ROMMask=I-1;ROMBank=1; }
if(!RAMMap[0]) RAMMask=0;
else { for(I=1;I<RAMBanks;I<<=1);RAMMask=I-1;RAMBank=0; }
ResetGB ();
if (!InitMachine())
return 0;
InitMachineDone=1;
Exit_PC=Z80(R);
return(1);
}
void TrashGB(void)
{
FILE *F;
int I;
if (InitMachineDone)
{
TrashMachine ();
if(Verbose) printf("EXITED at PC = %Xh.\n",Exit_PC);
}
if(SaveName)
{
if(Verbose) printf("\nOpening %s...",SaveName);
if(F=fopen(SaveName,"wb"))
{
if(Verbose) printf("writing...");
I=RAM[0x0147]==3? 0x2000:0x0200;
I=(fwrite(RAMMap[0]? RAMMap[0]:Page[5],1,I,F)==I);
if(Verbose) puts(I? "OK":"FAILED");
fclose(F);
}
else if(Verbose) puts("FAILED");
}
if(RAM) free(RAM);
for(I=1;ROMMap[I];I++) free(ROMMap[I]);
for(I=0;RAMMap[I];I++) free(RAMMap[I]);
if(SndName&&SndStream)
{
fputc (0xF0,SndStream);
fclose(SndStream);
}
}
int MachineInterrupt (void);
word Interrupt(void)
{
static byte LCDStates[11] = { 2,2,3,3,3,3,0,0,0,0,0 };
static byte LCount=0;
static byte UCount=1;
static byte ACount=0;
static byte dorefresh=1;
byte J,I;
++NumInts;
DIVREG++;LCount=(LCount+1)%11;
if(CURLINE<144) LCDSTAT=(LCDSTAT&0xFC)|LCDStates[LCount];
switch(LCount)
{
case 0:
/* Proceeding with line refresh */
break;
case 2:
/* Generating VBlank interrupt */
if(CURLINE==144)
if((ISWITCH&VBL_IFLAG)&&(LCDCONT&0x80))
{ IFLAGS=IMask=VBL_IFLAG;return(0x0040); }
return(0xFFFF);
case 5:
/* Generate serial IO interrupt */
if(SIOCount)
{
if (!--SIOCount)
{
SIOCONT&=0x7F;
if(ISWITCH&SIO_IFLAG)
{ IFLAGS=IMask=SIO_IFLAG; return 0x0058; }
}
}
return 0xFFFF;
case 7:
/* Generating HBlank interrupt */
if((LCDSTAT&0x08)&&(CURLINE<144))
if((ISWITCH&LCD_IFLAG)&&(LCDCONT&0x80))
{ IFLAGS=IMask=LCD_IFLAG;return(0x0048); }
return(0xFFFF);
case 10:
/* Generating timer interrupt */
if(TIMEFRQ&0x04)
{
TCount+=TStep;
if(TCount&0xFFFF0000)
{
unsigned L;
L=TIMECNT+(TCount>>16);
TCount&=0x0000FFFF;
if(L&0xFFFFFF00)
{
TIMECNT=TIMEMOD;
if(ISWITCH&TIM_IFLAG)
{ IFLAGS=IMask=TIM_IFLAG;return 0x0050; }
}
else TIMECNT=L;
}
return 0xFFFF;
}
default:
/* Only LCD state had to be changed */
return(0xFFFF);
}
/* Checking for line coincidence and refreshing screen */
J=0;
if(LineDelay)
{
if(dorefresh&&(CURLINE<144)) RefreshLine(CURLINE);
CURLINE=(CURLINE+1)%154;
if(CURLINE!=CMPLINE) LCDSTAT&=0xFB;
else { LCDSTAT|=0x04;J|=0x40; }
}
else
{
if(CURLINE!=CMPLINE) LCDSTAT&=0xFB;
else { LCDSTAT|=0x04;J|=0x40; }
CURLINE=(CURLINE+1)%154;
if(dorefresh&&(CURLINE<144)) RefreshLine(CURLINE);
}
/* If end of frame reached... */
if(CURLINE==144)
{
/* Set VBlank state */
LCDSTAT=(LCDSTAT&0xFC)|0x01;J|=0x10;
/* Update joystick */
JoyState=Joystick();
/* Autofire emulation */
ACount=(ACount+1)&0x07;
if(ACount>3)
{
if(AutoA) JoyState|=0x10;
if(AutoB) JoyState|=0x20;
}
/* Assign value to JOYPAD */
I=JOYPAD|0xCF;
if(!(I&0x10)) JOYPAD=I&(JoyState|0xF0);
if(!(I&0x20)) JOYPAD=I&((JoyState>>4)|0xF0);
/* Refresh screen if needed */
if (dorefresh)
RefreshScreen();
/* Sync the GB emulation */
switch (MachineInterrupt ())
{
case 0: if (UPeriod==0)
{
dorefresh=0;
break;
}
case 1: if (UPeriod==0)
{
dorefresh=1;
break;
}
default: if (!--UCount)
{
UCount=UPeriod;
dorefresh=1;
}
else
dorefresh=0;
break;
}
}
/* Generating LCD controller interrupt */
if((J&LCDSTAT)&&(ISWITCH&LCD_IFLAG)&&(LCDCONT&0x80)) IFLAGS|=LCD_IFLAG;
/* Determining interrupt address */
if(IFLAGS&EXT_IFLAG) { IMask=EXT_IFLAG;return(0x0060); }
if(IFLAGS&SIO_IFLAG) { IMask=SIO_IFLAG;return(0x0058); }
if(IFLAGS&TIM_IFLAG) { IMask=TIM_IFLAG;return(0x0050); }
if(IFLAGS&LCD_IFLAG) { IMask=LCD_IFLAG;return(0x0048); }
if(IFLAGS&VBL_IFLAG) { IMask=VBL_IFLAG;return(0x0040); }
/* No interrupt */
return(0xFFFF);
}
int AddCheat(char *Cheat)
{
static char Digits[]="0123456789ABCDEF";
int X1,X2;
if(CheatCount>=MAXCHEAT) return(0);
else
{
if((Cheat[3]!='-')||(Cheat[7]!='-')) return(0);
X1=strchr(Digits,toupper(Cheat[0]))-Digits;
X2=strchr(Digits,toupper(Cheat[1]))-Digits;
if((X1<0)||(X2<0)) return(0);
else Cheats[CheatCount].Value=X1*16+X2;
X1=strchr(Digits,toupper(Cheat[4]))-Digits;
X2=strchr(Digits,toupper(Cheat[5]))-Digits;
if((X1<0)||(X2<0)) return(0);
else Cheats[CheatCount].Address=X1*16+X2;
X1=strchr(Digits,toupper(Cheat[6]))-Digits;
X2=strchr(Digits,toupper(Cheat[2]))-Digits;
if((X1<0)||(X2<0)) return(0);
else Cheats[CheatCount].Address+=256*((15-X1)*16+X2);
X1=strchr(Digits,toupper(Cheat[10]))-Digits;
X2=strchr(Digits,toupper(Cheat[8]))-Digits;
if((X1<0)||(X2<0)) return(0);
X1=~(16*X2+X1);
X1=((X1>>2)&0x3F)|((X1<<6)&0xC0);
Cheats[CheatCount].Orig=X1^0x45;
if(Cheats[CheatCount].Address>=0x8000) return(0);
CheatCount++;return(1);
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -