📄 gb.c
字号:
/** VGB: portable GameBoy emulator ***************************/
/** **/
/** GB.c **/
/** **/
/** This file contains the portable part of the GameBoy **/
/** hardware emulation. See GB.h for #defines related to **/
/** drivers and GameBoy hardware. **/
/** **/
/** Copyright (C) Marat Fayzullin 1995,1996 **/
/** Marcel de Kogel 1996 **/
/** You are not allowed to distribute this software **/
/** commercially. Please, notify me, if you make any **/
/** changes to this file. **/
/*************************************************************/
#include "GB.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
byte Verbose = 1; /* Verboseness level */
byte *RAM,*Page[8]; /* RAM and pointers to Z80 address space 8x8kB) */
int VPeriod = 69905; /* Number of Z80 cycles between VBlanks */
byte UPeriod = 1; /* Number of VBlanks per screen update */
byte LineDelay = 1; /* When 1, screen refreshing is delayed */
byte CheckCRC = 1; /* When 1, VGB checks cartridge CRC on loading */
char *SaveName = NULL; /* .sav file name */
char *SndName = NULL; /* Soundtrack log file */
FILE *SndStream = NULL;
struct /* Cheat list to be filled before StartGB() */
{ /* is called */
byte Value,Orig;
word Address;
} Cheats[MAXCHEAT];
int CheatCount = 0; /* Number of cheats in the list */
byte SIOBuf; /* Next byte to output via the serial line */
byte SIOFlag; /* Bit0 -> SIO interrupt should be generated */
/* Bit1 -> Data should be sent out */
byte IMask; /* A mask to reset an event bit in IFLAGS */
byte MBCType; /* MBC type: 1 for MBC2, 0 for MBC1 */
byte *ROMMap[256]; /* Addresses of ROM banks */
byte ROMBank; /* Number of ROM bank currently used */
byte ROMMask; /* Mask for the ROM bank number */
int ROMBanks; /* Total number of ROM banks */
byte *RAMMap[256]; /* Addresses of RAM banks */
byte RAMBank; /* Number of RAM bank currently used */
byte RAMMask; /* Mask for the RAM bank number */
int RAMBanks; /* Total number of RAM banks */
byte JoyState = 0xFF; /* Joystick state: START.SELECT.B.A.D.U.L.R */
byte AutoA = 0; /* When 1, autofire emulation for button A */
byte AutoB = 0; /* When 1, autofire emulation for button B */
unsigned TCount,TStep; /* Timer counter and increment */
unsigned SIOCount; /* Serial I/O counter */
byte BPal[4]; /* Background palette */
byte SPal0[4],SPal1[4]; /* Sprite palettes */
byte *ChrGen; /* Character generator */
byte *BgdTab,*WndTab; /* Background and window character tables */
int IFreq=60; /* Interrupt Frequency in Hertz */
static unsigned NumInts=0;
static void WriteIntStamp (void)
{
if (NumInts>65535)
{
fputc (0xFF,SndStream);
fputc (NumInts,SndStream);
fputc (NumInts>>8,SndStream);
fputc (NumInts>>16,SndStream);
fputc (NumInts>>24,SndStream);
}
else
{
if (NumInts>255)
{
fputc (0xFE,SndStream);
fputc (NumInts,SndStream);
fputc (NumInts>>8,SndStream);
}
else
{
fputc (0xFD,SndStream);
fputc (NumInts,SndStream);
}
}
NumInts=0;
}
static void _Sound (byte Reg, byte Value)
{
if(SndStream)
{ WriteIntStamp();fputc(Reg,SndStream);fputc(Value,SndStream); }
Sound (Reg,Value);
}
static void WriteROM (unsigned A,byte V) __attribute__ ((regparm(3)));
static void WriteCartRAM (unsigned A,byte V) __attribute__ ((regparm(3)));
static void WriteRAM (unsigned A,byte V) __attribute__ ((regparm(3)));
static void WROMBank (unsigned A,byte V) __attribute__ ((regparm(3)));
static void WRAMBank (unsigned A,byte V) __attribute__ ((regparm(3)));
static void WriteIO (unsigned A,byte V) __attribute__ ((regparm(3)));
static void WriteCartRAM (unsigned A,byte V)
{
Page[A>>13][A&0x1FFF]=V;
}
static void WriteRAM (unsigned A,byte V)
{
RAM[A]=V;
}
static void WROMBank (unsigned A,byte V)
{
if(MBCType&&((A&0xFF00)!=0x2100)) return;
V&=ROMMask;
if(!V) V=1;
if(ROMMask&&(V!=ROMBank))
{
ROMBank=V;
Page[2]=ROMMap[V]? ROMMap[V]:RAM+0x4000;
Page[3]=Page[2]+0x2000;
if(Verbose&0x08) printf("ROM: Bank %d selected\n",V);
}
}
static void WriteROM (unsigned A,byte V)
{
if(Verbose&0x02) printf("Wrote %Xh to ROM at %Xh\n",V,A);
}
static void WRAMBank (unsigned A,byte V)
{
V&=RAMMask;
if(RAMMask&&!MBCType&&(RAMBank!=V))
{
RAMBank=V;
Page[5]=RAMMap[V]? RAMMap[V]:RAM+0xA000;
if(Verbose&0x08) printf("RAM: Bank %d selected\n",V);
}
}
static void WriteIO(unsigned A,byte V)
{
static unsigned long TPFreqs[] = { 4096,262144,65536,16384 };
byte *P;
switch(A)
{
case 0xFF00: JOYPAD=0xCF|V;
if(!(V&0x20)) JOYPAD&=(JoyState>>4)|0xF0;
if(!(V&0x10)) JOYPAD&=JoyState|0xF0;
return;
case 0xFF01: SIOBuf=V;SIOFlag|=0x02;return;
case 0xFF02: SIOCount=0;
if(V&0x80)
{
if ((V&1)==0) /* external clock */
{
if(SIOFlag&0x02? SIOSend(SIOBuf):SIOReceive(&SIODATA))
SIOCount=8;
}
else /* internal clock */
SIOCount=8;
SIOFlag=(SIOFlag&0xFD);
}
V|=0x7E;
break;
case 0xFF07: TStep=(TPFreqs[V&0x03]<<16)/(154*IFreq);
V|=0xF8;break;
case 0xFF0F: V&=0x1F;break;
case 0xFFFF: V&=0x1F;break;
case 0xFF46: P=RAM+0xFE00;A=(word)V<<8;
for(V=0;V<0xA0;V++) *P++=M_RDMEM(A++);
return;
case 0xFF41: V=(V&0xF8)|(LCDSTAT&0x07);
break;
case 0xFF40: ChrGen=RAM+(V&0x10? 0x8000:0x8800);
BgdTab=RAM+(V&0x08? 0x9C00:0x9800);
WndTab=RAM+(V&0x40? 0x9C00:0x9800);
break;
case 0xFF44: V=0;break;
case 0xFF47: BPal[0]=V&0x03;
BPal[1]=(V&0x0C)>>2;
BPal[2]=(V&0x30)>>4;
BPal[3]=(V&0xC0)>>6;
break;
case 0xFF48: SPal0[0]=V&0x03;
SPal0[1]=(V&0x0C)>>2;
SPal0[2]=(V&0x30)>>4;
SPal0[3]=(V&0xC0)>>6;
break;
case 0xFF49: SPal1[0]=V&0x03;
SPal1[1]=(V&0x0C)>>2;
SPal1[2]=(V&0x30)>>4;
SPal1[3]=(V&0xC0)>>6;
break;
default: if((A>=0xFF10)&&(A<=0xFF26))
_Sound(A-0xFF10,V);
}
RAM[A]=V;
}
WriteMemFn WriteMemFnTable[256]= {
/* 0000-1FFF ROM */
WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,
WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,
WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,
WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,
/* 2000-3FFF ROM Bank Select */
WROMBank,WROMBank,WROMBank,WROMBank,WROMBank,WROMBank,WROMBank,WROMBank,
WROMBank,WROMBank,WROMBank,WROMBank,WROMBank,WROMBank,WROMBank,WROMBank,
WROMBank,WROMBank,WROMBank,WROMBank,WROMBank,WROMBank,WROMBank,WROMBank,
WROMBank,WROMBank,WROMBank,WROMBank,WROMBank,WROMBank,WROMBank,WROMBank,
/* 4000-5FFF RAM Bank Select */
WRAMBank,WRAMBank,WRAMBank,WRAMBank,WRAMBank,WRAMBank,WRAMBank,WRAMBank,
WRAMBank,WRAMBank,WRAMBank,WRAMBank,WRAMBank,WRAMBank,WRAMBank,WRAMBank,
WRAMBank,WRAMBank,WRAMBank,WRAMBank,WRAMBank,WRAMBank,WRAMBank,WRAMBank,
WRAMBank,WRAMBank,WRAMBank,WRAMBank,WRAMBank,WRAMBank,WRAMBank,WRAMBank,
/* 6000-7FFF ROM */
WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,
WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,
WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,
WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,
/* 8000-9FFF RAM */
WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,
WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,
WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,
WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,
/* A000-BFFF Cartridge RAM */
WriteCartRAM,WriteCartRAM,WriteCartRAM,WriteCartRAM,WriteCartRAM,WriteCartRAM,WriteCartRAM,WriteCartRAM,
WriteCartRAM,WriteCartRAM,WriteCartRAM,WriteCartRAM,WriteCartRAM,WriteCartRAM,WriteCartRAM,WriteCartRAM,
WriteCartRAM,WriteCartRAM,WriteCartRAM,WriteCartRAM,WriteCartRAM,WriteCartRAM,WriteCartRAM,WriteCartRAM,
WriteCartRAM,WriteCartRAM,WriteCartRAM,WriteCartRAM,WriteCartRAM,WriteCartRAM,WriteCartRAM,WriteCartRAM,
/* C000-FEFF RAM */
WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,
WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,
WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,
WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,
WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,
WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,
WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,
WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,
/* FF00-FFFF I/O */
WriteIO
};
void M_WRMEM (unsigned A,byte V)
{
(*WriteMemFnTable[A>>8])(A,V);
}
byte M_RDMEM(unsigned A)
{
return (Page[A>>13][A&0x1FFF]);
}
byte M_RDMEM_OPCODE (void)
{
byte V;
V=M_RDMEM (R.PC.D);
R.PC.W++;
return V;
}
void ResetGB (void)
{
int I;
if(RAMMap[0]) Page[5]=RAMMap[0];
if(ROMMap[1]) Page[2]=ROMMap[1];
Page[3]=Page[2]+0x2000;
TStep=32768;TCount=0;
ChrGen=RAM+0x8800;BgdTab=WndTab=RAM+0x9800;
LCDCONT=0x81;LCDSTAT=0x00;
CURLINE=0x00;CMPLINE=0xFF;
IFLAGS=ISWITCH=0x00;
TIMECNT=TIMEMOD=0x01;
TIMEFRQ=0xF8;
SIODATA=0x00;
SIOCONT=0x7E;
SIOBuf=SIOFlag=0x00;
JoyState=0xFF;
for(I=0;I<4;I++) SPal0[I]=SPal1[I]=BPal[I]=I;
BGRDPAL=SPR0PAL=SPR1PAL=0xE4;
/* Initialise the timer */
M_WRMEM (0xFF07,RAM[0xFF07]);
ResetZ80(&R);
}
static int InitMachineDone=0;
static word Exit_PC;
int StartGB(char *CartName)
{
static char *CartTypes[] =
{
"ROM ONLY","ROM+MBC1","ROM+MBC1+RAM",
"ROM+MBC1+RAM+BATTERY","UNKNOWN",
"ROM+MBC2","ROM+MBC2+BATTERY"
};
/*** Following are some known manufacturer codes *************************/
static struct { word Code;char *Name; } Companies[] =
{
{ 0x3301,"Nintendo" },{ 0x7901,"Accolade" },{ 0xA400,"Konami" },
{ 0x6701,"Ocean" },{ 0x5601,"LJN" },{ 0x9900,"ARC?" },
{ 0x0101,"Nintendo" },{ 0x0801,"Capcom" },{ 0x0100,"Nintendo" },
{ 0xBB01,"SunSoft" },{ 0xA401,"Konami" },{ 0xAF01,"Namcot?" },
{ 0x4901,"Irem" },{ 0x9C01,"Imagineer" },{ 0xA600,"Kawada?" },
{ 0xB101,"Nexoft" },{ 0x5101,"Acclaim" },{ 0x6001,"Titus" },
{ 0xB601,"HAL" },{ 0x3300,"Nintendo" },{ 0x0B00,"Coconuts?" },
{ 0x5401,"Gametek" },{ 0x7F01,"Kemco?" },{ 0xC001,"Taito" },
{ 0xEB01,"Atlus" },{ 0xE800,"Asmik?" },{ 0xDA00,"Tomy?" },
{ 0xB100,"ASCII?" },{ 0xEB00,"Atlus" },{ 0xC000,"Taito" },
{ 0x9C00,"Imagineer" },{ 0xC201,"Kemco?" },{ 0xD101,"Sofel?" },
{ 0x6101,"Virgin" },{ 0xBB00,"SunSoft" },{ 0xCE01,"FCI?" },
{ 0xB400,"Enix?" },{ 0xBD01,"Imagesoft" },{ 0x0A01,"Jaleco?" },
{ 0xDF00,"Altron?" },{ 0xA700,"Takara?" },{ 0xEE00,"IGS?" },
{ 0x8300,"Lozc?" },{ 0x5001,"Absolute?" },{ 0xDD00,"NCS?" },
{ 0xE500,"Epoch?" },{ 0xCB00,"VAP?" },{ 0x8C00,"Vic Tokai" },
{ 0xC200,"Kemco?" },{ 0xBF00,"Sammy?" },
{ 0x1800,"Hudson Soft" },{ 0xCA01,"Palcom/Ultra" },
{ 0xCA00,"Palcom/Ultra" },{ 0xC500,"Data East?" },
{ 0xA900,"Technos Japan?" },{ 0xD900,"Banpresto?" },
{ 0x7201,"Broderbund?" },{ 0x7A01,"Triffix Entertainment?" },
{ 0xE100,"Towachiki?" },{ 0x9300,"Tsuburava?" },
{ 0xC600,"Tonkin House?" },{ 0xCE00,"Pony Canyon" },
{ 0x7001,"Infogrames?" },{ 0x8B01,"Bullet-Proof Software?" },
{ 0x5501,"Park Place?" },{ 0xEA00,"King Records?" },
{ 0x5D01,"Tradewest?" },{ 0x6F01,"ElectroBrain?" },
{ 0xAA01,"Broderbund?" },{ 0xC301,"SquareSoft" },
{ 0x5201,"Activision?" },{ 0x5A01,"Bitmap Brothers/Mindscape" },
{ 0x5301,"American Sammy" },{ 0x4701,"Spectrum Holobyte" },
{ 0x1801,"Hudson Soft"},{ 0x0000,NULL }
};
int Checksum,I,J,*T;
FILE *F;
char *P,S[50];
word A;
int rambanks[4]= { 0,1,1,4 };
/*** STARTUP CODE starts here: ***/
T=(int *)"\01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
#ifdef LSB_FIRST
if(*T!=1)
{
puts("********** This machine is high-endian. *********");
puts("Take #define LSB_FIRST out and compile VGB again.");
return(0);
}
#else
if(*T==1)
{
puts("********* This machine is low-endian. *********");
puts("Insert #define LSB_FIRST and compile VGB again.");
return(0);
}
#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -