#include #include #include #include #include "../System.h" #include "../NLS.h" #include "gb.h" #include "gbCheats.h" #include "gbGlobals.h" #include "gbMemory.h" #include "gbSGB.h" #include "gbSound.h" #include "../Util.h" #ifdef __GNUC__ #define _stricmp strcasecmp #endif extern u8 *pix; extern bool speedup; bool gbUpdateSizes(); bool inBios = false; // debugging bool memorydebug = false; char gbBuffer[2048]; extern u16 gbLineMix[160]; // mappers void (*mapper)(u16,u8) = NULL; void (*mapperRAM)(u16,u8) = NULL; u8 (*mapperReadRAM)(u16) = NULL; void (*mapperUpdateClock)() = NULL; // registers gbRegister PC; gbRegister SP; gbRegister AF; gbRegister BC; gbRegister DE; gbRegister HL; u16 IFF = 0; // 0xff04 u8 register_DIV = 0; // 0xff05 u8 register_TIMA = 0; // 0xff06 u8 register_TMA = 0; // 0xff07 u8 register_TAC = 0; // 0xff0f u8 register_IF = 0; // 0xff40 u8 register_LCDC = 0; // 0xff41 u8 register_STAT = 0; // 0xff42 u8 register_SCY = 0; // 0xff43 u8 register_SCX = 0; // 0xff44 u8 register_LY = 0; // 0xff45 u8 register_LYC = 0; // 0xff46 u8 register_DMA = 0; // 0xff4a u8 register_WY = 0; // 0xff4b u8 register_WX = 0; // 0xff4f u8 register_VBK = 0; // 0xff51 u8 register_HDMA1 = 0; // 0xff52 u8 register_HDMA2 = 0; // 0xff53 u8 register_HDMA3 = 0; // 0xff54 u8 register_HDMA4 = 0; // 0xff55 u8 register_HDMA5 = 0; // 0xff70 u8 register_SVBK = 0; // 0xffff u8 register_IE = 0; // ticks definition int GBDIV_CLOCK_TICKS = 64; int GBLCD_MODE_0_CLOCK_TICKS = 51; int GBLCD_MODE_1_CLOCK_TICKS = 1140; int GBLCD_MODE_2_CLOCK_TICKS = 20; int GBLCD_MODE_3_CLOCK_TICKS = 43; int GBLY_INCREMENT_CLOCK_TICKS = 114; int GBTIMER_MODE_0_CLOCK_TICKS = 256; int GBTIMER_MODE_1_CLOCK_TICKS = 4; int GBTIMER_MODE_2_CLOCK_TICKS = 16; int GBTIMER_MODE_3_CLOCK_TICKS = 64; int GBSERIAL_CLOCK_TICKS = 128; int GBSYNCHRONIZE_CLOCK_TICKS = 52920; // state variables // general int clockTicks = 0; bool gbSystemMessage = false; int gbGBCColorType = 0; int gbHardware = 0; int gbRomType = 0; int gbRemainingClockTicks = 0; int gbOldClockTicks = 0; int gbIntBreak = 0; int gbInterruptLaunched = 0; u8 gbCheatingDevice = 0; // 1 = GS, 2 = GG // breakpoint bool breakpoint = false; // interrupt int gbInt48Signal = 0; int gbInterruptWait = 0; // serial int gbSerialOn = 0; int gbSerialTicks = 0; int gbSerialBits = 0; // timer bool gbTimerOn = false; int gbTimerTicks = GBTIMER_MODE_0_CLOCK_TICKS; int gbTimerClockTicks = GBTIMER_MODE_0_CLOCK_TICKS; int gbTimerMode = 0; bool gbIncreased = false; // The internal timer is always active, and it is // not reset by writing to register_TIMA/TMA, but by // writing to register_DIV... int gbInternalTimer = 0x55; const u8 gbTimerMask [4] = {0xff, 0x3, 0xf, 0x3f}; const u8 gbTimerBug [8] = {0x80, 0x80, 0x02, 0x02, 0x0, 0xff, 0x0, 0xff}; bool gbTimerModeChange = false; bool gbTimerOnChange = false; // lcd bool gbScreenOn = true; int gbLcdMode = 2; int gbLcdModeDelayed = 2; int gbLcdTicks = GBLCD_MODE_2_CLOCK_TICKS-1; int gbLcdTicksDelayed = GBLCD_MODE_2_CLOCK_TICKS; int gbLcdLYIncrementTicks = 114; int gbLcdLYIncrementTicksDelayed = 115; int gbScreenTicks = 0; u8 gbSCYLine[300]; u8 gbSCXLine[300]; u8 gbBgpLine[300]; u8 gbObp0Line [300]; u8 gbObp1Line [300]; u8 gbSpritesTicks [300]; u8 oldRegister_WY; bool gbLYChangeHappened = false; bool gbLCDChangeHappened = false; int gbLine99Ticks = 1; int gbRegisterLYLCDCOffOn = 0; int inUseRegister_WY = 0; // Used to keep track of the line that ellapse // when screen is off int gbWhiteScreen = 0; bool gbBlackScreen = false; int register_LCDCBusy = 0; // div int gbDivTicks = GBDIV_CLOCK_TICKS; // cgb int gbVramBank = 0; int gbWramBank = 1; //sgb bool gbSgbResetFlag = false; // gbHdmaDestination is 0x99d0 on startup (tested on HW) // but I'm not sure what gbHdmaSource is... int gbHdmaSource = 0x99d0; int gbHdmaDestination = 0x99d0; int gbHdmaBytes = 0x0000; int gbHdmaOn = 0; int gbSpeed = 0; // frame counting int gbFrameCount = 0; int gbFrameSkip = 0; int gbFrameSkipCount = 0; // timing u32 gbLastTime = 0; u32 gbElapsedTime = 0; u32 gbTimeNow = 0; int gbSynchronizeTicks = GBSYNCHRONIZE_CLOCK_TICKS; // emulator features int gbBattery = 0; bool gbBatteryError = false; int gbCaptureNumber = 0; bool gbCapture = false; bool gbCapturePrevious = false; int gbJoymask[4] = { 0, 0, 0, 0 }; u8 gbRamFill = 0xff; int gbRomSizes[] = { 0x00008000, // 32K 0x00010000, // 64K 0x00020000, // 128K 0x00040000, // 256K 0x00080000, // 512K 0x00100000, // 1024K 0x00200000, // 2048K 0x00400000, // 4096K 0x00800000 // 8192K }; int gbRomSizesMasks[] = { 0x00007fff, 0x0000ffff, 0x0001ffff, 0x0003ffff, 0x0007ffff, 0x000fffff, 0x001fffff, 0x003fffff, 0x007fffff }; int gbRamSizes[6] = { 0x00000000, // 0K 0x00002000, // 2K // Changed to 2000 to avoid problems with gbMemoryMap... 0x00002000, // 8K 0x00008000, // 32K 0x00020000, // 128K 0x00010000 // 64K }; int gbRamSizesMasks[6] = { 0x00000000, 0x000007ff, 0x00001fff, 0x00007fff, 0x0001ffff, 0x0000ffff }; int gbCycles[] = { // 0 1 2 3 4 5 6 7 8 9 a b c d e f 1, 3, 2, 2, 1, 1, 2, 1, 5, 2, 2, 2, 1, 1, 2, 1, // 0 1, 3, 2, 2, 1, 1, 2, 1, 3, 2, 2, 2, 1, 1, 2, 1, // 1 2, 3, 2, 2, 1, 1, 2, 1, 2, 2, 2, 2, 1, 1, 2, 1, // 2 2, 3, 2, 2, 3, 3, 3, 1, 2, 2, 2, 2, 1, 1, 2, 1, // 3 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 4 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 5 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 6 2, 2, 2, 2, 2, 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, // 7 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 8 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 9 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // a 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // b 2, 3, 3, 4, 3, 4, 2, 4, 2, 4, 3, 2, 3, 6, 2, 4, // c 2, 3, 3, 1, 3, 4, 2, 4, 2, 4, 3, 1, 3, 1, 2, 4, // d 3, 3, 2, 1, 1, 4, 2, 4, 4, 1, 4, 1, 1, 1, 2, 4, // e 3, 3, 2, 1, 1, 4, 2, 4, 3, 2, 4, 1, 0, 1, 2, 4 // f }; int gbCyclesCB[] = { // 0 1 2 3 4 5 6 7 8 9 a b c d e f 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 0 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 1 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 2 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 3 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // 4 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // 5 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // 6 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // 7 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 8 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 9 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // a 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // b 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // c 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // d 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // e 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2 // f }; u16 DAATable[] = { 0x0080,0x0100,0x0200,0x0300,0x0400,0x0500,0x0600,0x0700, 0x0800,0x0900,0x1020,0x1120,0x1220,0x1320,0x1420,0x1520, 0x1000,0x1100,0x1200,0x1300,0x1400,0x1500,0x1600,0x1700, 0x1800,0x1900,0x2020,0x2120,0x2220,0x2320,0x2420,0x2520, 0x2000,0x2100,0x2200,0x2300,0x2400,0x2500,0x2600,0x2700, 0x2800,0x2900,0x3020,0x3120,0x3220,0x3320,0x3420,0x3520, 0x3000,0x3100,0x3200,0x3300,0x3400,0x3500,0x3600,0x3700, 0x3800,0x3900,0x4020,0x4120,0x4220,0x4320,0x4420,0x4520, 0x4000,0x4100,0x4200,0x4300,0x4400,0x4500,0x4600,0x4700, 0x4800,0x4900,0x5020,0x5120,0x5220,0x5320,0x5420,0x5520, 0x5000,0x5100,0x5200,0x5300,0x5400,0x5500,0x5600,0x5700, 0x5800,0x5900,0x6020,0x6120,0x6220,0x6320,0x6420,0x6520, 0x6000,0x6100,0x6200,0x6300,0x6400,0x6500,0x6600,0x6700, 0x6800,0x6900,0x7020,0x7120,0x7220,0x7320,0x7420,0x7520, 0x7000,0x7100,0x7200,0x7300,0x7400,0x7500,0x7600,0x7700, 0x7800,0x7900,0x8020,0x8120,0x8220,0x8320,0x8420,0x8520, 0x8000,0x8100,0x8200,0x8300,0x8400,0x8500,0x8600,0x8700, 0x8800,0x8900,0x9020,0x9120,0x9220,0x9320,0x9420,0x9520, 0x9000,0x9100,0x9200,0x9300,0x9400,0x9500,0x9600,0x9700, 0x9800,0x9900,0x00B0,0x0130,0x0230,0x0330,0x0430,0x0530, 0x0090,0x0110,0x0210,0x0310,0x0410,0x0510,0x0610,0x0710, 0x0810,0x0910,0x1030,0x1130,0x1230,0x1330,0x1430,0x1530, 0x1010,0x1110,0x1210,0x1310,0x1410,0x1510,0x1610,0x1710, 0x1810,0x1910,0x2030,0x2130,0x2230,0x2330,0x2430,0x2530, 0x2010,0x2110,0x2210,0x2310,0x2410,0x2510,0x2610,0x2710, 0x2810,0x2910,0x3030,0x3130,0x3230,0x3330,0x3430,0x3530, 0x3010,0x3110,0x3210,0x3310,0x3410,0x3510,0x3610,0x3710, 0x3810,0x3910,0x4030,0x4130,0x4230,0x4330,0x4430,0x4530, 0x4010,0x4110,0x4210,0x4310,0x4410,0x4510,0x4610,0x4710, 0x4810,0x4910,0x5030,0x5130,0x5230,0x5330,0x5430,0x5530, 0x5010,0x5110,0x5210,0x5310,0x5410,0x5510,0x5610,0x5710, 0x5810,0x5910,0x6030,0x6130,0x6230,0x6330,0x6430,0x6530, 0x6010,0x6110,0x6210,0x6310,0x6410,0x6510,0x6610,0x6710, 0x6810,0x6910,0x7030,0x7130,0x7230,0x7330,0x7430,0x7530, 0x7010,0x7110,0x7210,0x7310,0x7410,0x7510,0x7610,0x7710, 0x7810,0x7910,0x8030,0x8130,0x8230,0x8330,0x8430,0x8530, 0x8010,0x8110,0x8210,0x8310,0x8410,0x8510,0x8610,0x8710, 0x8810,0x8910,0x9030,0x9130,0x9230,0x9330,0x9430,0x9530, 0x9010,0x9110,0x9210,0x9310,0x9410,0x9510,0x9610,0x9710, 0x9810,0x9910,0xA030,0xA130,0xA230,0xA330,0xA430,0xA530, 0xA010,0xA110,0xA210,0xA310,0xA410,0xA510,0xA610,0xA710, 0xA810,0xA910,0xB030,0xB130,0xB230,0xB330,0xB430,0xB530, 0xB010,0xB110,0xB210,0xB310,0xB410,0xB510,0xB610,0xB710, 0xB810,0xB910,0xC030,0xC130,0xC230,0xC330,0xC430,0xC530, 0xC010,0xC110,0xC210,0xC310,0xC410,0xC510,0xC610,0xC710, 0xC810,0xC910,0xD030,0xD130,0xD230,0xD330,0xD430,0xD530, 0xD010,0xD110,0xD210,0xD310,0xD410,0xD510,0xD610,0xD710, 0xD810,0xD910,0xE030,0xE130,0xE230,0xE330,0xE430,0xE530, 0xE010,0xE110,0xE210,0xE310,0xE410,0xE510,0xE610,0xE710, 0xE810,0xE910,0xF030,0xF130,0xF230,0xF330,0xF430,0xF530, 0xF010,0xF110,0xF210,0xF310,0xF410,0xF510,0xF610,0xF710, 0xF810,0xF910,0x00B0,0x0130,0x0230,0x0330,0x0430,0x0530, 0x0090,0x0110,0x0210,0x0310,0x0410,0x0510,0x0610,0x0710, 0x0810,0x0910,0x1030,0x1130,0x1230,0x1330,0x1430,0x1530, 0x1010,0x1110,0x1210,0x1310,0x1410,0x1510,0x1610,0x1710, 0x1810,0x1910,0x2030,0x2130,0x2230,0x2330,0x2430,0x2530, 0x2010,0x2110,0x2210,0x2310,0x2410,0x2510,0x2610,0x2710, 0x2810,0x2910,0x3030,0x3130,0x3230,0x3330,0x3430,0x3530, 0x3010,0x3110,0x3210,0x3310,0x3410,0x3510,0x3610,0x3710, 0x3810,0x3910,0x4030,0x4130,0x4230,0x4330,0x4430,0x4530, 0x4010,0x4110,0x4210,0x4310,0x4410,0x4510,0x4610,0x4710, 0x4810,0x4910,0x5030,0x5130,0x5230,0x5330,0x5430,0x5530, 0x5010,0x5110,0x5210,0x5310,0x5410,0x5510,0x5610,0x5710, 0x5810,0x5910,0x6030,0x6130,0x6230,0x6330,0x6430,0x6530, 0x0600,0x0700,0x0800,0x0900,0x0A00,0x0B00,0x0C00,0x0D00, 0x0E00,0x0F00,0x1020,0x1120,0x1220,0x1320,0x1420,0x1520, 0x1600,0x1700,0x1800,0x1900,0x1A00,0x1B00,0x1C00,0x1D00, 0x1E00,0x1F00,0x2020,0x2120,0x2220,0x2320,0x2420,0x2520, 0x2600,0x2700,0x2800,0x2900,0x2A00,0x2B00,0x2C00,0x2D00, 0x2E00,0x2F00,0x3020,0x3120,0x3220,0x3320,0x3420,0x3520, 0x3600,0x3700,0x3800,0x3900,0x3A00,0x3B00,0x3C00,0x3D00, 0x3E00,0x3F00,0x4020,0x4120,0x4220,0x4320,0x4420,0x4520, 0x4600,0x4700,0x4800,0x4900,0x4A00,0x4B00,0x4C00,0x4D00, 0x4E00,0x4F00,0x5020,0x5120,0x5220,0x5320,0x5420,0x5520, 0x5600,0x5700,0x5800,0x5900,0x5A00,0x5B00,0x5C00,0x5D00, 0x5E00,0x5F00,0x6020,0x6120,0x6220,0x6320,0x6420,0x6520, 0x6600,0x6700,0x6800,0x6900,0x6A00,0x6B00,0x6C00,0x6D00, 0x6E00,0x6F00,0x7020,0x7120,0x7220,0x7320,0x7420,0x7520, 0x7600,0x7700,0x7800,0x7900,0x7A00,0x7B00,0x7C00,0x7D00, 0x7E00,0x7F00,0x8020,0x8120,0x8220,0x8320,0x8420,0x8520, 0x8600,0x8700,0x8800,0x8900,0x8A00,0x8B00,0x8C00,0x8D00, 0x8E00,0x8F00,0x9020,0x9120,0x9220,0x9320,0x9420,0x9520, 0x9600,0x9700,0x9800,0x9900,0x9A00,0x9B00,0x9C00,0x9D00, 0x9E00,0x9F00,0x00B0,0x0130,0x0230,0x0330,0x0430,0x0530, 0x0610,0x0710,0x0810,0x0910,0x0A10,0x0B10,0x0C10,0x0D10, 0x0E10,0x0F10,0x1030,0x1130,0x1230,0x1330,0x1430,0x1530, 0x1610,0x1710,0x1810,0x1910,0x1A10,0x1B10,0x1C10,0x1D10, 0x1E10,0x1F10,0x2030,0x2130,0x2230,0x2330,0x2430,0x2530, 0x2610,0x2710,0x2810,0x2910,0x2A10,0x2B10,0x2C10,0x2D10, 0x2E10,0x2F10,0x3030,0x3130,0x3230,0x3330,0x3430,0x3530, 0x3610,0x3710,0x3810,0x3910,0x3A10,0x3B10,0x3C10,0x3D10, 0x3E10,0x3F10,0x4030,0x4130,0x4230,0x4330,0x4430,0x4530, 0x4610,0x4710,0x4810,0x4910,0x4A10,0x4B10,0x4C10,0x4D10, 0x4E10,0x4F10,0x5030,0x5130,0x5230,0x5330,0x5430,0x5530, 0x5610,0x5710,0x5810,0x5910,0x5A10,0x5B10,0x5C10,0x5D10, 0x5E10,0x5F10,0x6030,0x6130,0x6230,0x6330,0x6430,0x6530, 0x6610,0x6710,0x6810,0x6910,0x6A10,0x6B10,0x6C10,0x6D10, 0x6E10,0x6F10,0x7030,0x7130,0x7230,0x7330,0x7430,0x7530, 0x7610,0x7710,0x7810,0x7910,0x7A10,0x7B10,0x7C10,0x7D10, 0x7E10,0x7F10,0x8030,0x8130,0x8230,0x8330,0x8430,0x8530, 0x8610,0x8710,0x8810,0x8910,0x8A10,0x8B10,0x8C10,0x8D10, 0x8E10,0x8F10,0x9030,0x9130,0x9230,0x9330,0x9430,0x9530, 0x9610,0x9710,0x9810,0x9910,0x9A10,0x9B10,0x9C10,0x9D10, 0x9E10,0x9F10,0xA030,0xA130,0xA230,0xA330,0xA430,0xA530, 0xA610,0xA710,0xA810,0xA910,0xAA10,0xAB10,0xAC10,0xAD10, 0xAE10,0xAF10,0xB030,0xB130,0xB230,0xB330,0xB430,0xB530, 0xB610,0xB710,0xB810,0xB910,0xBA10,0xBB10,0xBC10,0xBD10, 0xBE10,0xBF10,0xC030,0xC130,0xC230,0xC330,0xC430,0xC530, 0xC610,0xC710,0xC810,0xC910,0xCA10,0xCB10,0xCC10,0xCD10, 0xCE10,0xCF10,0xD030,0xD130,0xD230,0xD330,0xD430,0xD530, 0xD610,0xD710,0xD810,0xD910,0xDA10,0xDB10,0xDC10,0xDD10, 0xDE10,0xDF10,0xE030,0xE130,0xE230,0xE330,0xE430,0xE530, 0xE610,0xE710,0xE810,0xE910,0xEA10,0xEB10,0xEC10,0xED10, 0xEE10,0xEF10,0xF030,0xF130,0xF230,0xF330,0xF430,0xF530, 0xF610,0xF710,0xF810,0xF910,0xFA10,0xFB10,0xFC10,0xFD10, 0xFE10,0xFF10,0x00B0,0x0130,0x0230,0x0330,0x0430,0x0530, 0x0610,0x0710,0x0810,0x0910,0x0A10,0x0B10,0x0C10,0x0D10, 0x0E10,0x0F10,0x1030,0x1130,0x1230,0x1330,0x1430,0x1530, 0x1610,0x1710,0x1810,0x1910,0x1A10,0x1B10,0x1C10,0x1D10, 0x1E10,0x1F10,0x2030,0x2130,0x2230,0x2330,0x2430,0x2530, 0x2610,0x2710,0x2810,0x2910,0x2A10,0x2B10,0x2C10,0x2D10, 0x2E10,0x2F10,0x3030,0x3130,0x3230,0x3330,0x3430,0x3530, 0x3610,0x3710,0x3810,0x3910,0x3A10,0x3B10,0x3C10,0x3D10, 0x3E10,0x3F10,0x4030,0x4130,0x4230,0x4330,0x4430,0x4530, 0x4610,0x4710,0x4810,0x4910,0x4A10,0x4B10,0x4C10,0x4D10, 0x4E10,0x4F10,0x5030,0x5130,0x5230,0x5330,0x5430,0x5530, 0x5610,0x5710,0x5810,0x5910,0x5A10,0x5B10,0x5C10,0x5D10, 0x5E10,0x5F10,0x6030,0x6130,0x6230,0x6330,0x6430,0x6530, 0x00C0,0x0140,0x0240,0x0340,0x0440,0x0540,0x0640,0x0740, 0x0840,0x0940,0x0440,0x0540,0x0640,0x0740,0x0840,0x0940, 0x1040,0x1140,0x1240,0x1340,0x1440,0x1540,0x1640,0x1740, 0x1840,0x1940,0x1440,0x1540,0x1640,0x1740,0x1840,0x1940, 0x2040,0x2140,0x2240,0x2340,0x2440,0x2540,0x2640,0x2740, 0x2840,0x2940,0x2440,0x2540,0x2640,0x2740,0x2840,0x2940, 0x3040,0x3140,0x3240,0x3340,0x3440,0x3540,0x3640,0x3740, 0x3840,0x3940,0x3440,0x3540,0x3640,0x3740,0x3840,0x3940, 0x4040,0x4140,0x4240,0x4340,0x4440,0x4540,0x4640,0x4740, 0x4840,0x4940,0x4440,0x4540,0x4640,0x4740,0x4840,0x4940, 0x5040,0x5140,0x5240,0x5340,0x5440,0x5540,0x5640,0x5740, 0x5840,0x5940,0x5440,0x5540,0x5640,0x5740,0x5840,0x5940, 0x6040,0x6140,0x6240,0x6340,0x6440,0x6540,0x6640,0x6740, 0x6840,0x6940,0x6440,0x6540,0x6640,0x6740,0x6840,0x6940, 0x7040,0x7140,0x7240,0x7340,0x7440,0x7540,0x7640,0x7740, 0x7840,0x7940,0x7440,0x7540,0x7640,0x7740,0x7840,0x7940, 0x8040,0x8140,0x8240,0x8340,0x8440,0x8540,0x8640,0x8740, 0x8840,0x8940,0x8440,0x8540,0x8640,0x8740,0x8840,0x8940, 0x9040,0x9140,0x9240,0x9340,0x9440,0x9540,0x9640,0x9740, 0x9840,0x9940,0x3450,0x3550,0x3650,0x3750,0x3850,0x3950, 0x4050,0x4150,0x4250,0x4350,0x4450,0x4550,0x4650,0x4750, 0x4850,0x4950,0x4450,0x4550,0x4650,0x4750,0x4850,0x4950, 0x5050,0x5150,0x5250,0x5350,0x5450,0x5550,0x5650,0x5750, 0x5850,0x5950,0x5450,0x5550,0x5650,0x5750,0x5850,0x5950, 0x6050,0x6150,0x6250,0x6350,0x6450,0x6550,0x6650,0x6750, 0x6850,0x6950,0x6450,0x6550,0x6650,0x6750,0x6850,0x6950, 0x7050,0x7150,0x7250,0x7350,0x7450,0x7550,0x7650,0x7750, 0x7850,0x7950,0x7450,0x7550,0x7650,0x7750,0x7850,0x7950, 0x8050,0x8150,0x8250,0x8350,0x8450,0x8550,0x8650,0x8750, 0x8850,0x8950,0x8450,0x8550,0x8650,0x8750,0x8850,0x8950, 0x9050,0x9150,0x9250,0x9350,0x9450,0x9550,0x9650,0x9750, 0x9850,0x9950,0x9450,0x9550,0x9650,0x9750,0x9850,0x9950, 0xA050,0xA150,0xA250,0xA350,0xA450,0xA550,0xA650,0xA750, 0xA850,0xA950,0xA450,0xA550,0xA650,0xA750,0xA850,0xA950, 0xB050,0xB150,0xB250,0xB350,0xB450,0xB550,0xB650,0xB750, 0xB850,0xB950,0xB450,0xB550,0xB650,0xB750,0xB850,0xB950, 0xC050,0xC150,0xC250,0xC350,0xC450,0xC550,0xC650,0xC750, 0xC850,0xC950,0xC450,0xC550,0xC650,0xC750,0xC850,0xC950, 0xD050,0xD150,0xD250,0xD350,0xD450,0xD550,0xD650,0xD750, 0xD850,0xD950,0xD450,0xD550,0xD650,0xD750,0xD850,0xD950, 0xE050,0xE150,0xE250,0xE350,0xE450,0xE550,0xE650,0xE750, 0xE850,0xE950,0xE450,0xE550,0xE650,0xE750,0xE850,0xE950, 0xF050,0xF150,0xF250,0xF350,0xF450,0xF550,0xF650,0xF750, 0xF850,0xF950,0xF450,0xF550,0xF650,0xF750,0xF850,0xF950, 0x00D0,0x0150,0x0250,0x0350,0x0450,0x0550,0x0650,0x0750, 0x0850,0x0950,0x0450,0x0550,0x0650,0x0750,0x0850,0x0950, 0x1050,0x1150,0x1250,0x1350,0x1450,0x1550,0x1650,0x1750, 0x1850,0x1950,0x1450,0x1550,0x1650,0x1750,0x1850,0x1950, 0x2050,0x2150,0x2250,0x2350,0x2450,0x2550,0x2650,0x2750, 0x2850,0x2950,0x2450,0x2550,0x2650,0x2750,0x2850,0x2950, 0x3050,0x3150,0x3250,0x3350,0x3450,0x3550,0x3650,0x3750, 0x3850,0x3950,0x3450,0x3550,0x3650,0x3750,0x3850,0x3950, 0x4050,0x4150,0x4250,0x4350,0x4450,0x4550,0x4650,0x4750, 0x4850,0x4950,0x4450,0x4550,0x4650,0x4750,0x4850,0x4950, 0x5050,0x5150,0x5250,0x5350,0x5450,0x5550,0x5650,0x5750, 0x5850,0x5950,0x5450,0x5550,0x5650,0x5750,0x5850,0x5950, 0x6050,0x6150,0x6250,0x6350,0x6450,0x6550,0x6650,0x6750, 0x6850,0x6950,0x6450,0x6550,0x6650,0x6750,0x6850,0x6950, 0x7050,0x7150,0x7250,0x7350,0x7450,0x7550,0x7650,0x7750, 0x7850,0x7950,0x7450,0x7550,0x7650,0x7750,0x7850,0x7950, 0x8050,0x8150,0x8250,0x8350,0x8450,0x8550,0x8650,0x8750, 0x8850,0x8950,0x8450,0x8550,0x8650,0x8750,0x8850,0x8950, 0x9050,0x9150,0x9250,0x9350,0x9450,0x9550,0x9650,0x9750, 0x9850,0x9950,0x9450,0x9550,0x9650,0x9750,0x9850,0x9950, 0xFA60,0xFB60,0xFC60,0xFD60,0xFE60,0xFF60,0x00C0,0x0140, 0x0240,0x0340,0x0440,0x0540,0x0640,0x0740,0x0840,0x0940, 0x0A60,0x0B60,0x0C60,0x0D60,0x0E60,0x0F60,0x1040,0x1140, 0x1240,0x1340,0x1440,0x1540,0x1640,0x1740,0x1840,0x1940, 0x1A60,0x1B60,0x1C60,0x1D60,0x1E60,0x1F60,0x2040,0x2140, 0x2240,0x2340,0x2440,0x2540,0x2640,0x2740,0x2840,0x2940, 0x2A60,0x2B60,0x2C60,0x2D60,0x2E60,0x2F60,0x3040,0x3140, 0x3240,0x3340,0x3440,0x3540,0x3640,0x3740,0x3840,0x3940, 0x3A60,0x3B60,0x3C60,0x3D60,0x3E60,0x3F60,0x4040,0x4140, 0x4240,0x4340,0x4440,0x4540,0x4640,0x4740,0x4840,0x4940, 0x4A60,0x4B60,0x4C60,0x4D60,0x4E60,0x4F60,0x5040,0x5140, 0x5240,0x5340,0x5440,0x5540,0x5640,0x5740,0x5840,0x5940, 0x5A60,0x5B60,0x5C60,0x5D60,0x5E60,0x5F60,0x6040,0x6140, 0x6240,0x6340,0x6440,0x6540,0x6640,0x6740,0x6840,0x6940, 0x6A60,0x6B60,0x6C60,0x6D60,0x6E60,0x6F60,0x7040,0x7140, 0x7240,0x7340,0x7440,0x7540,0x7640,0x7740,0x7840,0x7940, 0x7A60,0x7B60,0x7C60,0x7D60,0x7E60,0x7F60,0x8040,0x8140, 0x8240,0x8340,0x8440,0x8540,0x8640,0x8740,0x8840,0x8940, 0x8A60,0x8B60,0x8C60,0x8D60,0x8E60,0x8F60,0x9040,0x9140, 0x9240,0x9340,0x3450,0x3550,0x3650,0x3750,0x3850,0x3950, 0x3A70,0x3B70,0x3C70,0x3D70,0x3E70,0x3F70,0x4050,0x4150, 0x4250,0x4350,0x4450,0x4550,0x4650,0x4750,0x4850,0x4950, 0x4A70,0x4B70,0x4C70,0x4D70,0x4E70,0x4F70,0x5050,0x5150, 0x5250,0x5350,0x5450,0x5550,0x5650,0x5750,0x5850,0x5950, 0x5A70,0x5B70,0x5C70,0x5D70,0x5E70,0x5F70,0x6050,0x6150, 0x6250,0x6350,0x6450,0x6550,0x6650,0x6750,0x6850,0x6950, 0x6A70,0x6B70,0x6C70,0x6D70,0x6E70,0x6F70,0x7050,0x7150, 0x7250,0x7350,0x7450,0x7550,0x7650,0x7750,0x7850,0x7950, 0x7A70,0x7B70,0x7C70,0x7D70,0x7E70,0x7F70,0x8050,0x8150, 0x8250,0x8350,0x8450,0x8550,0x8650,0x8750,0x8850,0x8950, 0x8A70,0x8B70,0x8C70,0x8D70,0x8E70,0x8F70,0x9050,0x9150, 0x9250,0x9350,0x9450,0x9550,0x9650,0x9750,0x9850,0x9950, 0x9A70,0x9B70,0x9C70,0x9D70,0x9E70,0x9F70,0xA050,0xA150, 0xA250,0xA350,0xA450,0xA550,0xA650,0xA750,0xA850,0xA950, 0xAA70,0xAB70,0xAC70,0xAD70,0xAE70,0xAF70,0xB050,0xB150, 0xB250,0xB350,0xB450,0xB550,0xB650,0xB750,0xB850,0xB950, 0xBA70,0xBB70,0xBC70,0xBD70,0xBE70,0xBF70,0xC050,0xC150, 0xC250,0xC350,0xC450,0xC550,0xC650,0xC750,0xC850,0xC950, 0xCA70,0xCB70,0xCC70,0xCD70,0xCE70,0xCF70,0xD050,0xD150, 0xD250,0xD350,0xD450,0xD550,0xD650,0xD750,0xD850,0xD950, 0xDA70,0xDB70,0xDC70,0xDD70,0xDE70,0xDF70,0xE050,0xE150, 0xE250,0xE350,0xE450,0xE550,0xE650,0xE750,0xE850,0xE950, 0xEA70,0xEB70,0xEC70,0xED70,0xEE70,0xEF70,0xF050,0xF150, 0xF250,0xF350,0xF450,0xF550,0xF650,0xF750,0xF850,0xF950, 0xFA70,0xFB70,0xFC70,0xFD70,0xFE70,0xFF70,0x00D0,0x0150, 0x0250,0x0350,0x0450,0x0550,0x0650,0x0750,0x0850,0x0950, 0x0A70,0x0B70,0x0C70,0x0D70,0x0E70,0x0F70,0x1050,0x1150, 0x1250,0x1350,0x1450,0x1550,0x1650,0x1750,0x1850,0x1950, 0x1A70,0x1B70,0x1C70,0x1D70,0x1E70,0x1F70,0x2050,0x2150, 0x2250,0x2350,0x2450,0x2550,0x2650,0x2750,0x2850,0x2950, 0x2A70,0x2B70,0x2C70,0x2D70,0x2E70,0x2F70,0x3050,0x3150, 0x3250,0x3350,0x3450,0x3550,0x3650,0x3750,0x3850,0x3950, 0x3A70,0x3B70,0x3C70,0x3D70,0x3E70,0x3F70,0x4050,0x4150, 0x4250,0x4350,0x4450,0x4550,0x4650,0x4750,0x4850,0x4950, 0x4A70,0x4B70,0x4C70,0x4D70,0x4E70,0x4F70,0x5050,0x5150, 0x5250,0x5350,0x5450,0x5550,0x5650,0x5750,0x5850,0x5950, 0x5A70,0x5B70,0x5C70,0x5D70,0x5E70,0x5F70,0x6050,0x6150, 0x6250,0x6350,0x6450,0x6550,0x6650,0x6750,0x6850,0x6950, 0x6A70,0x6B70,0x6C70,0x6D70,0x6E70,0x6F70,0x7050,0x7150, 0x7250,0x7350,0x7450,0x7550,0x7650,0x7750,0x7850,0x7950, 0x7A70,0x7B70,0x7C70,0x7D70,0x7E70,0x7F70,0x8050,0x8150, 0x8250,0x8350,0x8450,0x8550,0x8650,0x8750,0x8850,0x8950, 0x8A70,0x8B70,0x8C70,0x8D70,0x8E70,0x8F70,0x9050,0x9150, 0x9250,0x9350,0x9450,0x9550,0x9650,0x9750,0x9850,0x9950, }; u8 ZeroTable[256] = { 0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0 }; #define GBSAVE_GAME_VERSION_1 1 #define GBSAVE_GAME_VERSION_2 2 #define GBSAVE_GAME_VERSION_3 3 #define GBSAVE_GAME_VERSION_4 4 #define GBSAVE_GAME_VERSION_5 5 #define GBSAVE_GAME_VERSION_6 6 #define GBSAVE_GAME_VERSION_7 7 #define GBSAVE_GAME_VERSION_8 8 #define GBSAVE_GAME_VERSION_9 9 #define GBSAVE_GAME_VERSION_10 10 #define GBSAVE_GAME_VERSION_11 11 #define GBSAVE_GAME_VERSION_12 12 #define GBSAVE_GAME_VERSION GBSAVE_GAME_VERSION_12 int inline gbGetValue(int min,int max,int v) { return (int)(min+(float)(max-min)*(2.0*(v/31.0)-(v/31.0)*(v/31.0))); } void gbGenFilter() { for (int r=0;r<32;r++) { for (int g=0;g<32;g++) { for (int b=0;b<32;b++) { int nr=gbGetValue(gbGetValue(4,14,g), gbGetValue(24,29,g),r)-4; int ng=gbGetValue(gbGetValue(4+gbGetValue(0,5,r), 14+gbGetValue(0,3,r),b), gbGetValue(24+gbGetValue(0,3,r), 29+gbGetValue(0,1,r),b),g)-4; int nb=gbGetValue(gbGetValue(4+gbGetValue(0,5,r), 14+gbGetValue(0,3,r),g), gbGetValue(24+gbGetValue(0,3,r), 29+gbGetValue(0,1,r),g),b)-4; gbColorFilter[(b<<10)|(g<<5)|r]=(nb<<10)|(ng<<5)|nr; } } } } bool gbIsGameboyRom(char * file) { if(strlen(file) > 4) { char * p = strrchr(file,'.'); if(p != NULL) { if(_stricmp(p, ".gb") == 0) return true; if(_stricmp(p, ".gbc") == 0) return true; if(_stricmp(p, ".cgb") == 0) return true; if(_stricmp(p, ".sgb") == 0) return true; } } return false; } void gbCopyMemory(u16 d, u16 s, int count) { while(count) { gbMemoryMap[d>>12][d & 0x0fff] = gbMemoryMap[s>>12][s & 0x0fff]; s++; d++; count--; } } void gbDoHdma() { gbCopyMemory((gbHdmaDestination & 0x1ff0) | 0x8000, gbHdmaSource & 0xfff0, 0x10); gbHdmaDestination += 0x10; if (gbHdmaDestination == 0xa000) gbHdmaDestination = 0x8000; gbHdmaSource += 0x10; if (gbHdmaSource == 0x8000) gbHdmaSource = 0xa000; register_HDMA2 = gbHdmaSource & 0xff; register_HDMA1 = gbHdmaSource>>8; register_HDMA4 = gbHdmaDestination & 0xff; register_HDMA3 = gbHdmaDestination>>8; gbHdmaBytes -= 0x10; gbMemory[0xff55] = --register_HDMA5; if(register_HDMA5 == 0xff) gbHdmaOn = 0; // We need to add the dmaClockticks for HDMA ! if(gbSpeed) gbDmaTicks = 17; else gbDmaTicks = 9; if (IFF & 0x80) gbDmaTicks++; } // fix for Harley and Lego Racers void gbCompareLYToLYC() { if(register_LCDC & 0x80) { if(register_LY == register_LYC) { // mark that we have a match register_STAT |= 4; // check if we need an interrupt if (register_STAT & 0x40) { // send LCD interrupt only if no interrupt 48h signal... if (!gbInt48Signal) { register_IF |=2; } gbInt48Signal |= 8; } } else // no match { register_STAT &= 0xfb; gbInt48Signal &=~8; } } } void gbWriteMemory(register u16 address, register u8 value) { if(address < 0x8000) { #ifndef FINAL_VERSION if(memorydebug && (address>0x3fff || address < 0x2000)) { log("Memory register write %04x=%02x PC=%04x\n", address, value, PC.W); } #endif if(mapper) (*mapper)(address, value); return; } if(address < 0xa000) { // No access to Vram during mode 3 // (used to emulate the gfx differences between GB & GBC-GBA/SP in Stunt Racer) if ((gbLcdModeDelayed !=3) || // This part is used to emulate a small difference between hardwares // (check 8-in-1's arrow on GBA/GBC to verify it) ((register_LY == 0) && ((gbHardware & 0xa) && (gbScreenOn==false) && (register_LCDC & 0x80)) && (gbLcdLYIncrementTicksDelayed ==(GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS)))) gbMemoryMap[address>>12][address&0x0fff] = value; return; } // Used for the mirroring of 0xC000 in 0xE000 if ((address >= 0xe000) && (address < 0xfe00)) address &= ~0x2000; if(address < 0xc000) { #ifndef FINAL_VERSION if(memorydebug) { log("Memory register write %04x=%02x PC=%04x\n", address, value, PC.W); } #endif // Is that a correct fix ??? (it used to be 'if (mapper)')... if(mapperRAM) (*mapperRAM)(address, value); return; } if(address < 0xfe00) { gbMemoryMap[address>>12][address & 0x0fff] = value; return; } // OAM not accessible during mode 2 & 3. if(address < 0xfea0) { if (((gbHardware & 0xa) && ((gbLcdMode | gbLcdModeDelayed) &2)) || ((gbHardware & 5) && (((gbLcdModeDelayed == 2) && (gbLcdTicksDelayed<=GBLCD_MODE_2_CLOCK_TICKS)) || (gbLcdModeDelayed == 3)))) return; else { gbMemory[address] = value; return; } } if((address > 0xfea0) && (address < 0xff00)){ // GBC allows reading/writing to that area gbMemory[address] = value; return; } switch(address & 0x00ff) { case 0x00: { gbMemory[0xff00] = ((gbMemory[0xff00] & 0xcf) | (value & 0x30) | 0xc0); if(gbSgbMode) { gbSgbDoBitTransfer(value); } return; } case 0x01: { gbMemory[0xff01] = value; return; } // serial control case 0x02: { gbSerialOn = (value & 0x80); gbMemory[0xff02] = value; if(gbSerialOn) { gbSerialTicks = GBSERIAL_CLOCK_TICKS; #ifdef LINK_EMULATION if(linkConnected) { if(value & 1) { linkSendByte(0x100|gbMemory[0xFF01]); Sleep(5); } } #endif } gbSerialBits = 0; return; } case 0x04: { // DIV register resets on any write // (not totally perfect, but better than nothing) gbMemory[0xff04] = register_DIV = 0; gbDivTicks = GBDIV_CLOCK_TICKS; // Another weird timer 'bug' : // Writing to DIV register resets the internal timer, // and can also increase TIMA/trigger an interrupt // in some cases... if (gbTimerOn && !(gbInternalTimer & (gbTimerClockTicks>>1))) { gbMemory[0xff05] = ++register_TIMA; if(register_TIMA == 0) { // timer overflow! // reload timer modulo gbMemory[0xff05] = register_TIMA = register_TMA; // flag interrupt gbMemory[0xff0f] = register_IF |= 4; } } gbInternalTimer = 0xff; return; } case 0x05: gbMemory[0xff05] = register_TIMA = value; return; case 0x06: gbMemory[0xff06] = register_TMA = value; return; // TIMER control case 0x07: { gbTimerModeChange = (((value & 3) != (register_TAC&3)) && (value & register_TAC & 4)) ? true : false; gbTimerOnChange = (((value ^ register_TAC) & 4) == 4) ? true : false; gbTimerOn = (value & 4) ? true : false; if (gbTimerOnChange || gbTimerModeChange) { gbTimerMode = value & 3; switch(gbTimerMode) { case 0: gbTimerClockTicks = GBTIMER_MODE_0_CLOCK_TICKS; break; case 1: gbTimerClockTicks = GBTIMER_MODE_1_CLOCK_TICKS; break; case 2: gbTimerClockTicks = GBTIMER_MODE_2_CLOCK_TICKS; break; case 3: gbTimerClockTicks = GBTIMER_MODE_3_CLOCK_TICKS; break; } } // This looks weird, but this emulates a bug in which register_TIMA // is increased when writing to register_TAC // (This fixes Korodice's long-delay between menus bug). if (gbTimerOnChange || gbTimerModeChange) { bool temp = false; if ((gbTimerOn && !gbTimerModeChange) && (gbTimerMode & 2) && !(gbInternalTimer & 0x80) && (gbInternalTimer & (gbTimerClockTicks>>1)) && !(gbInternalTimer & (gbTimerClockTicks>>5))) temp = true; else if ((!gbTimerOn && !gbTimerModeChange && gbTimerOnChange ) && ((gbTimerTicks-1) < (gbTimerClockTicks>>1))) temp = true; else if (gbTimerOn && gbTimerModeChange && !gbTimerOnChange) { switch(gbTimerMode & 3) { case 0x00: temp = false; break; case 0x01: if (((gbInternalTimer & 0x82) == 2) && (gbTimerTicks>(clockTicks+1))) temp = true; break; case 0x02: if (((gbInternalTimer & 0x88) == 0x8) && (gbTimerTicks>(clockTicks+1))) temp = true; break; case 0x03: if (((gbInternalTimer & 0xA0) == 0x20) && (gbTimerTicks>(clockTicks+1))) temp = true; break; } } if (temp) { gbMemory[0xff05] = ++register_TIMA; if((register_TIMA & 0xff) == 0) { // timer overflow! // reload timer modulo gbMemory[0xff05] = register_TIMA = register_TMA; // flag interrupt gbMemory[0xff0f] = register_IF |= 4; } } } gbMemory[0xff07] = register_TAC = value; return; } case 0x0f: { gbMemory[0xff0f] = register_IF = value; //gbMemory[0xff0f] = register_IE |= value; return; } case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x16: case 0x17: case 0x18: case 0x19: case 0x1a: case 0x1b: case 0x1c: case 0x1d: case 0x1e: case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: { if (gbMemory[NR52] & 0x80) { SOUND_EVENT(address,value); return; } } case 0x26: { SOUND_EVENT(address,value); return; } case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37: case 0x38: case 0x39: case 0x3a: case 0x3b: case 0x3c: case 0x3d: case 0x3e: case 0x3f: { SOUND_EVENT(address,value); //gbMemory[address] = value; return; } case 0x40: { int lcdChange = (register_LCDC & 0x80) ^ (value & 0x80); // don't draw the window if it was not enabled and not being drawn before if(!(register_LCDC & 0x20) && (value & 0x20) && gbWindowLine == -1 && register_LY > register_WY) gbWindowLine = 146; // 007 fix : don't draw the first window's 1st line if it's enable 'too late' // (ie. if register_LY == register_WY when enabling it) // and move it to the next line else if (!(register_LCDC & 0x20) && (value & 0x20) && (register_LY == register_WY)) gbWindowLine = -2; gbMemory[0xff40] = register_LCDC = value; if(lcdChange) { if((value & 0x80) && (!register_LCDCBusy)) { // if (!gbWhiteScreen && !gbSgbMask) // systemDrawScreen(); gbRegisterLYLCDCOffOn = (register_LY + 144) % 154; gbLcdTicks = GBLCD_MODE_2_CLOCK_TICKS - (gbSpeed ? 2 : 1); gbLcdTicksDelayed = GBLCD_MODE_2_CLOCK_TICKS - (gbSpeed ? 1 : 0); gbLcdLYIncrementTicks = GBLY_INCREMENT_CLOCK_TICKS - (gbSpeed ? 2 : 1); gbLcdLYIncrementTicksDelayed = GBLY_INCREMENT_CLOCK_TICKS - (gbSpeed ? 1 : 0); gbLcdMode = 2; gbLcdModeDelayed = 2; gbMemory[0xff41] = register_STAT = (register_STAT & 0xfc) | 2; gbMemory[0xff44] = register_LY = 0x00; gbInt48Signal = 0; gbLYChangeHappened = false; gbLCDChangeHappened = false; gbWindowLine = 146; oldRegister_WY = 146; // Fix for Namco Gallery Vol.2 // (along with updating register_LCDC at the start of 'case 0x40') : if(register_STAT & 0x20) { // send LCD interrupt only if no interrupt 48h signal... if (!gbInt48Signal) { gbMemory[0xff0f] = register_IF |= 2; } gbInt48Signal |= 4; } gbCompareLYToLYC(); } else { register_LCDCBusy = clockTicks+6; //used to update the screen with white lines when it's off. //(it looks strange, but it's pretty accurate) gbWhiteScreen = 0; gbScreenTicks = ((150-register_LY)*GBLY_INCREMENT_CLOCK_TICKS + (49<<(gbSpeed ? 1 : 0))); // disable the screen rendering gbScreenOn = false; gbLcdTicks = 0; gbLcdMode = 0; gbLcdModeDelayed = 0; gbMemory[0xff41] = register_STAT &= 0xfc; gbInt48Signal = 0; } } return; } // STAT case 0x41: { //register_STAT = (register_STAT & 0x87) | // (value & 0x7c); gbMemory[0xff41] = register_STAT = (value & 0xf8) | (register_STAT & 0x07); // fix ? // GB bug from Devrs FAQ // proper fix gbInt48Signal &= ((register_STAT>>3) & 0xF); if((register_LCDC & 0x80)) { if ((register_STAT & 0x08) && (gbLcdMode == 0)) { if (!gbInt48Signal) { gbMemory[0xff0f] = register_IF |=2; } gbInt48Signal |= 1; } if ((register_STAT & 0x10) && (gbLcdMode == 1)) { if (!gbInt48Signal) { gbMemory[0xff0f] = register_IF |=2; } gbInt48Signal |= 2; } if ((register_STAT & 0x20) && (gbLcdMode == 2)) { if (!gbInt48Signal) { gbMemory[0xff0f] = register_IF |=2; } gbInt48Signal |= 4; } gbCompareLYToLYC(); gbMemory[0xff0f] = register_IF; gbMemory[0xff41] = register_STAT; } return; } // SCY case 0x42: { int temp = -1; if ((gbLcdMode == 3) || (gbLcdModeDelayed == 3)) temp = ((GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS) - gbLcdLYIncrementTicks); if (temp >=0) { for (int i=temp<<(gbSpeed ? 1 : 2);i<300;i++) if (temp < 300) gbSCYLine[i] = value; } else memset(gbSCYLine, value, sizeof(gbSCYLine)); gbMemory[0xff42] = register_SCY = value; return; } // SCX case 0x43: { int temp = -1; if (gbLcdModeDelayed == 3) temp = ((GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS) - gbLcdLYIncrementTicksDelayed); if (temp >=0) { for (int i=temp<<(gbSpeed ? 1 : 2);i<300;i++) if (temp < 300) gbSCXLine[i] = value; } else memset(gbSCXLine, value, sizeof(gbSCXLine)); gbMemory[0xff43] = register_SCX = value; return; } // LY case 0x44: { // read only return; } // LYC case 0x45: { if (register_LYC != value) { gbMemory[0xff45] = register_LYC = value; if(register_LCDC & 0x80) { gbCompareLYToLYC(); } } return; } // DMA! case 0x46: { int source = value * 0x0100; gbCopyMemory(0xfe00, source, 0xa0); gbMemory[0xff46] = register_DMA = value; return; } // BGP case 0x47: { int temp = -1; gbMemory[0xff47] = value; if (gbLcdModeDelayed == 3) temp = ((GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS) - gbLcdLYIncrementTicksDelayed); if (temp >=0) { for (int i=temp<<(gbSpeed ? 1 : 2);i<300;i++) if (temp < 300) gbBgpLine[i] = value; } else memset(gbBgpLine,value,sizeof(gbBgpLine)); gbBgp[0] = value & 0x03; gbBgp[1] = (value & 0x0c)>>2; gbBgp[2] = (value & 0x30)>>4; gbBgp[3] = (value & 0xc0)>>6; break; } // OBP0 case 0x48: { int temp = -1; gbMemory[0xff48] = value; if (gbLcdModeDelayed == 3) temp = ((GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS) - gbLcdLYIncrementTicksDelayed); if (temp >=0) { for (int i=temp<<(gbSpeed ? 1 : 2);i<300;i++) if (temp < 300) gbObp0Line[i] = value; } else memset(gbObp0Line,value,sizeof(gbObp0Line)); gbObp0[0] = value & 0x03; gbObp0[1] = (value & 0x0c)>>2; gbObp0[2] = (value & 0x30)>>4; gbObp0[3] = (value & 0xc0)>>6; break; } // OBP1 case 0x49: { int temp = -1; gbMemory[0xff49] = value; if (gbLcdModeDelayed == 3) temp = ((GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS) - gbLcdLYIncrementTicksDelayed); if (temp >=0) { for (int i=temp<<(gbSpeed ? 1 : 2);i<300;i++) if (temp < 300) gbObp1Line[i] = value; } else memset(gbObp1Line,value,sizeof(gbObp1Line)); gbObp1[0] = value & 0x03; gbObp1[1] = (value & 0x0c)>>2; gbObp1[2] = (value & 0x30)>>4; gbObp1[3] = (value & 0xc0)>>6; break; } // WY case 0x4a: gbMemory[0xff4a] = register_WY = value; if ((register_LY <= register_WY) && ((gbWindowLine < 0) || (gbWindowLine>=144))) { gbWindowLine = -1; oldRegister_WY = register_WY; } return; // WX case 0x4b: gbMemory[0xff4b] = register_WX = value; return; // KEY1 case 0x4d: { if(gbCgbMode) { gbMemory[0xff4d] = (gbMemory[0xff4d] & 0x80) | (value & 1) | 0x7e; return; } } break; // VBK case 0x4f: { if(gbCgbMode) { value = value & 1; if(value == gbVramBank) return; int vramAddress = value * 0x2000; gbMemoryMap[0x08] = &gbVram[vramAddress]; gbMemoryMap[0x09] = &gbVram[vramAddress + 0x1000]; gbVramBank = value; register_VBK = value; } return; } break; // BOOTROM disable register (also gbCgbMode enabler if value & 0x10 ?) case 0x50 : { if (useBios && inBios && !skipBios && (value & 1)) { gbMemoryMap[0x00] = &gbRom[0x0000]; memcpy ((u8 *)(gbRom+0x100), (u8 *)(gbMemory + 0x100), 0xF00); inBios = false; } } // HDMA1 case 0x51: { if(gbCgbMode) { if(value > 0x7f && value < 0xa0) value = 0; gbHdmaSource = (value << 8) | (gbHdmaSource & 0xf0); register_HDMA1 = value; return; } } break; // HDMA2 case 0x52: { if(gbCgbMode) { value = value & 0xf0; gbHdmaSource = (gbHdmaSource & 0xff00) | (value); register_HDMA2 = value; return; } } break; // HDMA3 case 0x53: { if(gbCgbMode) { value = value & 0x1f; gbHdmaDestination = (value << 8) | (gbHdmaDestination & 0xf0); gbHdmaDestination |= 0x8000; register_HDMA3 = value; return; } } break; // HDMA4 case 0x54: { if(gbCgbMode) { value = value & 0xf0; gbHdmaDestination = (gbHdmaDestination & 0x1f00) | value; gbHdmaDestination |= 0x8000; register_HDMA4 = value; return; } } break; // HDMA5 case 0x55: { if(gbCgbMode) { gbHdmaBytes = 16 + (value & 0x7f) * 16; if(gbHdmaOn) { if(value & 0x80) { gbMemory[0xff55] = register_HDMA5 = (value & 0x7f); } else { register_HDMA5 = 0xff; gbHdmaOn = 0; } } else { if(value & 0x80) { gbHdmaOn = 1; gbMemory[0xff55] = register_HDMA5 = value & 0x7f; if(gbLcdModeDelayed == 0) gbDoHdma(); } else { // we need to take the time it takes to complete the transfer into // account... according to GB DEV FAQs, the setup time is the same // for single and double speed, but the actual transfer takes the // same time if(gbSpeed) gbDmaTicks = 2+16 * ((value & 0x7f) +1); else gbDmaTicks = 1+8 * ((value & 0x7f)+1); gbCopyMemory((gbHdmaDestination & 0x1ff0) | 0x8000, gbHdmaSource & 0xfff0, gbHdmaBytes); gbHdmaDestination += gbHdmaBytes; gbHdmaSource += gbHdmaBytes; gbMemory[0xff51] = register_HDMA1 = 0xff;// = (gbHdmaSource >> 8) & 0xff; gbMemory[0xff52] = register_HDMA2 = 0xff;// = gbHdmaSource & 0xf0; gbMemory[0xff53] = register_HDMA3 = 0xff;// = ((gbHdmaDestination - 0x8000) >> 8) & 0x1f; gbMemory[0xff54] = register_HDMA4 = 0xff;// = gbHdmaDestination & 0xf0; gbMemory[0xff55] = register_HDMA5 = 0xff; } } return; } } break; // BCPS case 0x68: { if(gbCgbMode) { int paletteIndex = (value & 0x3f) >> 1; int paletteHiLo = (value & 0x01); gbMemory[0xff68] = value; gbMemory[0xff69] = (paletteHiLo ? (gbPalette[paletteIndex] >> 8) : (gbPalette[paletteIndex] & 0x00ff)); return; } } break; // BCPD case 0x69: { if(gbCgbMode) { int v = gbMemory[0xff68]; int paletteIndex = (v & 0x3f) >> 1; int paletteHiLo = (v & 0x01); // No access to gbPalette during mode 3 (Color Panel Demo) // CAK - The following check has to be commented out for // colourised roms like Metroid 2 DX //if (((gbLcdModeDelayed != 3) && (!((gbLcdMode == 0) && (gbLcdTicks>=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-1)))) && (!gbSpeed)) || // (gbSpeed && ((gbLcdMode == 1) || (gbLcdMode == 2) || // ((gbLcdMode == 3) && (gbLcdTicks>(GBLCD_MODE_3_CLOCK_TICKS-2))) || // ((gbLcdMode == 0) && (gbLcdTicks<=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-2)))))) { gbMemory[0xff69] = value; gbPalette[paletteIndex] = (paletteHiLo ? ((value << 8) | (gbPalette[paletteIndex] & 0xff)) : ((gbPalette[paletteIndex] & 0xff00) | (value))) & 0x7fff; } if(gbMemory[0xff68] & 0x80) { int index = ((gbMemory[0xff68] & 0x3f) + 1) & 0x3f; gbMemory[0xff68] = (gbMemory[0xff68] & 0x80) | index; gbMemory[0xff69] = (index & 1 ? (gbPalette[index>>1] >> 8) : (gbPalette[index>>1] & 0x00ff)); } return; } } break; // OCPS case 0x6a: { if(gbCgbMode) { int paletteIndex = (value & 0x3f) >> 1; int paletteHiLo = (value & 0x01); paletteIndex += 32; gbMemory[0xff6a] = value; gbMemory[0xff6b] = (paletteHiLo ? (gbPalette[paletteIndex] >> 8) : (gbPalette[paletteIndex] & 0x00ff)); return; } } break; // OCPD case 0x6b: { if(gbCgbMode) { int v = gbMemory[0xff6a]; int paletteIndex = (v & 0x3f) >> 1; int paletteHiLo = (v & 0x01); paletteIndex += 32; // No access to gbPalette during mode 3 (Color Panel Demo) // CAK - The following check has to be commented out for // colourised roms like Metroid 2 DX //if (((gbLcdModeDelayed != 3) && (!((gbLcdMode == 0) && (gbLcdTicks>=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-1)))) && (!gbSpeed)) || // (gbSpeed && ((gbLcdMode == 1) || (gbLcdMode == 2) || // ((gbLcdMode == 3) && (gbLcdTicks>(GBLCD_MODE_3_CLOCK_TICKS-2))) || // ((gbLcdMode == 0) && (gbLcdTicks<=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-2)))))) { gbMemory[0xff6b] = value; gbPalette[paletteIndex] = (paletteHiLo ? ((value << 8) | (gbPalette[paletteIndex] & 0xff)) : ((gbPalette[paletteIndex] & 0xff00) | (value))) & 0x7fff; } if(gbMemory[0xff6a] & 0x80) { int index = ((gbMemory[0xff6a] & 0x3f) + 1) & 0x3f; gbMemory[0xff6a] = (gbMemory[0xff6a] & 0x80) | index; gbMemory[0xff6b] = (index & 1 ? (gbPalette[(index>>1) + 32] >> 8) : (gbPalette[(index>>1) + 32] & 0x00ff)); } return; } } break; case 0x6c: { gbMemory[0xff6c] = 0xfe | value; return; } // SVBK case 0x70: { if(gbCgbMode) { value = value & 7; int bank = value; if(value == 0) bank = 1; if(bank == gbWramBank) return; int wramAddress = bank * 0x1000; gbMemoryMap[0x0d] = &gbWram[wramAddress]; gbWramBank = bank; gbMemory[0xff70] = register_SVBK = value; return; } } case 0x75:{ gbMemory[0xff75] = 0x8f | value; return; } case 0xff: { gbMemory[0xffff] = register_IE = value; return; } } if(address < 0xff80) { gbMemory[address] = value; return; } gbMemory[address] = value; } u8 gbReadOpcode(register u16 address) { if(gbCheatMap[address]) return gbCheatRead(address); if(address < 0x8000) return gbMemoryMap[address>>12][address&0x0fff]; if(address < 0xa000) { // A lot of 'ugly' checks... But only way to emulate this particular behaviour... if (((gbHardware & 0xa) && ((gbLcdModeDelayed !=3) || ((register_LY == 0) && (gbScreenOn==false) && (register_LCDC & 0x80)) && (gbLcdLYIncrementTicksDelayed ==(GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS)))) || ((gbHardware & 0x5) && (gbLcdModeDelayed !=3) && ((gbLcdMode !=3) || ((register_LY == 0) && ((gbScreenOn==false) && (register_LCDC & 0x80)) && (gbLcdLYIncrementTicks ==(GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS)))))) return gbMemoryMap[address>>12][address&0x0fff]; return 0xff; } // Used for the mirroring of 0xC000 in 0xE000 if ((address >= 0xe000) && (address < 0xfe00)) address &= ~0x2000; switch(address & 0xf000) { case 0x0a: case 0x0b: if(mapperReadRAM) return mapperReadRAM(address); break; case 0x0f: if(address > 0xff00) { switch(address & 0x00ff) { case 0x02: return (gbMemory[0xff02]); case 0x03: return (0xff); case 0x04: return register_DIV; case 0x05: return register_TIMA; case 0x06: return register_TMA; case 0x07: return (0xf8 | register_TAC); case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: case 0x0d: case 0x0e: return (0xff); case 0x0f: return (0xe0 | gbMemory[0xff0f]); case 0x40: return register_LCDC; case 0x41: // This is a GB/C only bug (ie. not GBA/SP). if ((gbHardware & 7) && (gbLcdMode == 2) && (gbLcdModeDelayed == 1) && (!gbSpeed)) return (0x80 | gbMemory[0xff41] & 0xFC); else return (0x80 | gbMemory[0xff41]); case 0x42: return register_SCY; case 0x43: return register_SCX; case 0x44: if (((gbHardware & 7) && ((gbLcdMode == 1) && (gbLcdTicks == 0x71))) || (!(register_LCDC && 0x80))) return 0; else return register_LY; case 0x45: return register_LYC; case 0x46: return register_DMA; case 0x4a: return register_WY; case 0x4b: return register_WX; case 0x4c: return 0xff; case 0x4f: return (0xfe | register_VBK); case 0x51: return register_HDMA1; case 0x52: return register_HDMA2; case 0x53: return register_HDMA3; case 0x54: return register_HDMA4; case 0x55: return register_HDMA5; case 0x68: case 0x6a: if (gbCgbMode) return (0x40 | gbMemory[address]); else return 0xc0; case 0x69: case 0x6b: if (gbCgbMode) { // No access to gbPalette during mode 3 (Color Panel Demo) //if (((gbLcdModeDelayed != 3) && (!((gbLcdMode == 0) && (gbLcdTicks>=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-1)))) && (!gbSpeed)) || // (gbSpeed && ((gbLcdMode == 1) || (gbLcdMode == 2) || // ((gbLcdMode == 3) && (gbLcdTicks>(GBLCD_MODE_3_CLOCK_TICKS-2))) || // ((gbLcdMode == 0) && (gbLcdTicks<=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-2)))))) return (gbMemory[address]); //else // return 0xff; } else return 0xff; case 0x70: if (gbCgbMode) return (0xf8 | register_SVBK); else return 0xff; case 0xff: return register_IE; } } // OAM not accessible during mode 2 & 3. if(((address >= 0xfe00) && (address<0xfea0)) && ((gbLcdMode | gbLcdModeDelayed) &2)) return 0xff; break; } if ((address >= 0xfea0) && (address < 0xff00)) { if (gbHardware & 1) return ((((address + ((address >> 4) - 0xfea)) >> 2) & 1) ? 0x00 : 0xff ); else if (gbHardware & 2) return gbMemoryMap[address>>12][address & 0x0fff]; else if (gbHardware & 4) return ((((address + ((address >> 4) - 0xfea)) >> 2) & 1) ? 0xff : 0x00 ); else if (gbHardware & 8) return ((address & 0xf0) |((address & 0xf0)>>4)); } return gbMemoryMap[address>>12][address & 0x0fff]; } u8 gbReadMemory(register u16 address) { if(gbCheatMap[address]) return gbCheatRead(address); if(address < 0x8000) return gbMemoryMap[address>>12][address&0x0fff]; if(address < 0xa000) { // A lot of 'ugly' checks... But only way to emulate this particular behaviour... if (((gbHardware & 0xa) && ((gbLcdModeDelayed !=3) || ((register_LY == 0) && (gbScreenOn==false) && (register_LCDC & 0x80)) && (gbLcdLYIncrementTicksDelayed ==(GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS)))) || ((gbHardware & 0x5) && (gbLcdModeDelayed !=3) && ((gbLcdMode !=3) || ((register_LY == 0) && ((gbScreenOn==false) && (register_LCDC & 0x80)) && (gbLcdLYIncrementTicks ==(GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS)))))) return gbMemoryMap[address>>12][address&0x0fff]; return 0xff; } if ((address >= 0xe000) && (address < 0xfe00)) address &= ~0x2000; if(address < 0xc000) { #ifndef FINAL_VERSION if(memorydebug) { log("Memory register read %04x PC=%04x\n", address, PC.W); } #endif // for the 2kb ram limit (fixes crash in shawu's story // but now its sram test fails, as the it expects 8kb and not 2kb... // So use the 'genericflashcard' option to fix it). if (address<=(0xa000+gbRamSizeMask)) { if(mapperReadRAM) return mapperReadRAM(address); return gbMemoryMap[address>>12][address & 0x0fff]; } return 0xff; } if(address >= 0xff00) { if ( address >= 0xFF10 && address <= 0xFF3F ) return gbSoundRead( address ); switch(address & 0x00ff) { case 0x00: { if(gbSgbMode) { gbSgbReadingController |= 4; gbSgbResetPacketState(); } int b = gbMemory[0xff00]; if((b & 0x30) == 0x20) { b &= 0xf0; int joy = 0; if(gbSgbMode && gbSgbMultiplayer) { switch(gbSgbNextController) { case 0x0f: joy = 0; break; case 0x0e: joy = 1; break; case 0x0d: joy = 2; break; case 0x0c: joy = 3; break; default: joy = 0; } } int joystate = gbJoymask[joy]; if(!(joystate & 128)) b |= 0x08; if(!(joystate & 64)) b |= 0x04; if(!(joystate & 32)) b |= 0x02; if(!(joystate & 16)) b |= 0x01; gbMemory[0xff00] = b; } else if((b & 0x30) == 0x10) { b &= 0xf0; int joy = 0; if(gbSgbMode && gbSgbMultiplayer) { switch(gbSgbNextController) { case 0x0f: joy = 0; break; case 0x0e: joy = 1; break; case 0x0d: joy = 2; break; case 0x0c: joy = 3; break; default: joy = 0; } } int joystate = gbJoymask[joy]; if(!(joystate & 8)) b |= 0x08; if(!(joystate & 4)) b |= 0x04; if(!(joystate & 2)) b |= 0x02; if(!(joystate & 1)) b |= 0x01; gbMemory[0xff00] = b; } else { if(gbSgbMode && gbSgbMultiplayer) { gbMemory[0xff00] = 0xf0 | gbSgbNextController; } else { gbMemory[0xff00] = 0xff; } } } return gbMemory[0xff00]; break; case 0x01: return gbMemory[0xff01]; case 0x02: return (gbMemory[0xff02]); case 0x04: return register_DIV; case 0x05: return register_TIMA; case 0x06: return register_TMA; case 0x07: return (0xf8 | register_TAC); case 0x0f: return (0xe0 | gbMemory[0xff0f]); case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37: case 0x38: case 0x39: case 0x3A: case 0x3B: case 0x3C: case 0x3D: case 0x3E: case 0x3F: if ((gbMemory[NR30] & 0x80) && (gbMemory[NR34] & 0x80)) return 0xFF; else return gbMemoryMap[address>>12][address & 0x0fff]; case 0x40: return register_LCDC; case 0x41: // This is a GB/C only bug (ie. not GBA/SP). if ((gbHardware & 7) && (gbLcdMode == 2) && (gbLcdModeDelayed == 1) && (!gbSpeed)) return (0x80 | gbMemory[0xff41] & 0xFC); else return (0x80 | gbMemory[0xff41]); case 0x42: return register_SCY; case 0x43: return register_SCX; case 0x44: if (((gbHardware & 7) && ((gbLcdMode == 1) && (gbLcdTicks == 0x71))) || (!(register_LCDC && 0x80))) return (0); else return register_LY; case 0x45: return register_LYC; case 0x46: return register_DMA; case 0x4a: return register_WY; case 0x4b: return register_WX; case 0x4f: return (0xfe | register_VBK); case 0x51: return register_HDMA1; case 0x52: return register_HDMA2; case 0x53: return register_HDMA3; case 0x54: return register_HDMA4; case 0x55: return register_HDMA5; case 0x68: case 0x6a: if (gbCgbMode) return (0x40 | gbMemory[address]); else return 0xc0; case 0x69: case 0x6b: if (gbCgbMode) { // No access to gbPalette during mode 3 (Color Panel Demo) //if (((gbLcdModeDelayed != 3) && (!((gbLcdMode == 0) && (gbLcdTicks>=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-1)))) && (!gbSpeed)) || // (gbSpeed && ((gbLcdMode == 1) || (gbLcdMode == 2) || // ((gbLcdMode == 3) && (gbLcdTicks>(GBLCD_MODE_3_CLOCK_TICKS-2))) || // ((gbLcdMode == 0) && (gbLcdTicks<=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-2)))))) return (gbMemory[address]); //else // return 0xff; } else return 0xff; case 0x70: if (gbCgbMode) return (0xf8 | register_SVBK); else return 0xff; case 0xff: return register_IE; } } // OAM not accessible during mode 2 & 3. if(((address >= 0xfe00) && (address<0xfea0)) && (((gbLcdMode | gbLcdModeDelayed) &2) && (!(gbSpeed && (gbHardware & 0x2) && !(gbLcdModeDelayed & 2) && (gbLcdMode == 2))) || (gbSpeed && (gbHardware & 0x2) && (gbLcdModeDelayed == 0) && (gbLcdTicksDelayed == (GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]))))) return 0xff; if ((address >= 0xfea0) && (address < 0xff00)) { if (gbHardware & 1) return ((((address + ((address >> 4) - 0xfea)) >> 2) & 1) ? 0x00 : 0xff ); else if (gbHardware & 2) return gbMemoryMap[address>>12][address & 0x0fff]; else if (gbHardware & 4) return ((((address + ((address >> 4) - 0xfea)) >> 2) & 1) ? 0xff : 0x00 ); else if (gbHardware & 8) return ((address & 0xf0) |((address & 0xf0)>>4)); } return gbMemoryMap[address>>12][address & 0x0fff]; } void gbVblank_interrupt() { gbCheatWrite(false); // Emulates GS codes. gbMemory[0xff0f] = register_IF &= 0xfe; gbWriteMemory(--SP.W, PC.B.B1); gbWriteMemory(--SP.W, PC.B.B0); PC.W = 0x40; } void gbLcd_interrupt() { gbMemory[0xff0f] = register_IF &= 0xfd; gbWriteMemory(--SP.W, PC.B.B1); gbWriteMemory(--SP.W, PC.B.B0); PC.W = 0x48; } void gbTimer_interrupt() { gbMemory[0xff0f] = register_IF &= 0xfb; gbWriteMemory(--SP.W, PC.B.B1); gbWriteMemory(--SP.W, PC.B.B0); PC.W = 0x50; } void gbSerial_interrupt() { gbMemory[0xff0f] = register_IF &= 0xf7; gbWriteMemory(--SP.W, PC.B.B1); gbWriteMemory(--SP.W, PC.B.B0); PC.W = 0x58; } void gbJoypad_interrupt() { gbMemory[0xff0f] = register_IF &= 0xef; gbWriteMemory(--SP.W, PC.B.B1); gbWriteMemory(--SP.W, PC.B.B0); PC.W = 0x60; } void gbSpeedSwitch() { gbBlackScreen = true; if(gbSpeed == 0) { gbSpeed = 1; GBLCD_MODE_0_CLOCK_TICKS = 51 * 2; GBLCD_MODE_1_CLOCK_TICKS = 1140 * 2; GBLCD_MODE_2_CLOCK_TICKS = 20 * 2; GBLCD_MODE_3_CLOCK_TICKS = 43 * 2; GBLY_INCREMENT_CLOCK_TICKS = 114 * 2; GBDIV_CLOCK_TICKS = 64; GBTIMER_MODE_0_CLOCK_TICKS = 256; GBTIMER_MODE_1_CLOCK_TICKS = 4; GBTIMER_MODE_2_CLOCK_TICKS = 16; GBTIMER_MODE_3_CLOCK_TICKS = 64; GBSERIAL_CLOCK_TICKS = 128 * 2; gbLcdTicks *= 2; gbLcdTicksDelayed *=2; gbLcdTicksDelayed--; gbLcdLYIncrementTicks *= 2; gbLcdLYIncrementTicksDelayed *= 2; gbLcdLYIncrementTicksDelayed--; gbSerialTicks *= 2; //SOUND_CLOCK_TICKS = soundQuality * 24 * 2; //soundTicks *= 2; gbLine99Ticks = 3; } else { gbSpeed = 0; GBLCD_MODE_0_CLOCK_TICKS = 51; GBLCD_MODE_1_CLOCK_TICKS = 1140; GBLCD_MODE_2_CLOCK_TICKS = 20; GBLCD_MODE_3_CLOCK_TICKS = 43; GBLY_INCREMENT_CLOCK_TICKS = 114; GBDIV_CLOCK_TICKS = 64; GBTIMER_MODE_0_CLOCK_TICKS = 256; GBTIMER_MODE_1_CLOCK_TICKS = 4; GBTIMER_MODE_2_CLOCK_TICKS = 16; GBTIMER_MODE_3_CLOCK_TICKS = 64; GBSERIAL_CLOCK_TICKS = 128; gbLcdTicks >>= 1; gbLcdTicksDelayed++; gbLcdTicksDelayed >>=1; gbLcdLYIncrementTicks >>= 1; gbLcdLYIncrementTicksDelayed++; gbLcdLYIncrementTicksDelayed >>= 1; gbSerialTicks /= 2; //SOUND_CLOCK_TICKS = soundQuality * 24; //soundTicks /= 2; gbLine99Ticks = 1; if (gbHardware & 8) gbLine99Ticks++; } gbDmaTicks += (134)*GBLY_INCREMENT_CLOCK_TICKS + (37<<(gbSpeed ? 1 : 0)); } bool CPUIsGBBios(const char * file) { if(strlen(file) > 4) { const char * p = strrchr(file,'.'); if(p != NULL) { if(_stricmp(p, ".gb") == 0) return true; if(_stricmp(p, ".bin") == 0) return true; if(_stricmp(p, ".bios") == 0) return true; if(_stricmp(p, ".rom") == 0) return true; } } return false; } void gbCPUInit(const char *biosFileName, bool useBiosFile) { useBios = false; if (useBiosFile) { /* int size = 0x100; if(utilLoad(biosFileName, CPUIsGBBios, bios, size)) { if(size == 0x100) useBios = true; else systemMessage(MSG_INVALID_BIOS_FILE_SIZE, N_("Invalid BOOTROM file size")); }*/ } } void gbGetHardwareType() { gbCgbMode = 0; gbSgbMode = 0; if(gbRom[0x143] & 0x80) { if((gbEmulatorType == 0) || gbEmulatorType == 1 || gbEmulatorType == 4) { gbCgbMode = 1; } } if((gbCgbMode == 0 ) && (gbRom[0x146] == 0x03)) { if(gbEmulatorType == 0 || gbEmulatorType == 2 || gbEmulatorType == 5) gbSgbMode = 1; } gbHardware = 1; // GB if (((gbCgbMode == 1) && (gbEmulatorType == 0)) || (gbEmulatorType == 1)) gbHardware = 2; // GBC else if (((gbSgbMode == 1) && (gbEmulatorType == 0)) || (gbEmulatorType == 2) || (gbEmulatorType == 5)) gbHardware = 4; // SGB(2) else if (gbEmulatorType == 4) gbHardware = 8; // GBA gbGBCColorType = 0; if (gbHardware & 8) // If GBA is selected, choose the GBA default settings. gbGBCColorType = 2; // (0 = GBC, 1 = GBA, 2 = GBASP) } void gbReset() { gbGetHardwareType(); oldRegister_WY = 146; gbInterruptLaunched = 0; if(gbCgbMode == 1) { if (gbVram == NULL) gbVram = (u8 *)malloc(0x4000); if (gbWram == NULL) gbWram = (u8 *)malloc(0x8000); memset(gbVram,0,0x4000); memset(gbPalette,0, 2*128); } else { if(gbVram != NULL) { free(gbVram); gbVram = NULL; } if(gbWram != NULL) { free(gbWram); gbWram = NULL; } } gbLYChangeHappened = false; gbLCDChangeHappened = false; gbBlackScreen = false; gbInterruptWait = 0; gbDmaTicks = 0; clockTicks = 0; if(gbSpeed) { gbSpeedSwitch(); gbMemory[0xff4d] = 0; } // clean Wram // This kinda emulates the startup state of Wram on GB/C (not very accurate, // but way closer to the reality than filling it with 00es or FFes). // On GBA/GBASP, it's kinda filled with random data. // In all cases, most of the 2nd bank is filled with 00s. // The starting data are important for some 'buggy' games, like Buster Brothers or // Karamuchou ha Oosawagi!. if (gbMemory != NULL) { memset(gbMemory,0xff, 65536); for (int temp = 0xC000; temp < 0xE000; temp++) if ((temp & 0x8) ^((temp & 0x800)>>8)) { if ((gbHardware & 0x02) && (gbGBCColorType == 0)) gbMemory[temp] = 0x0; else gbMemory[temp] = 0x0f; } else gbMemory[temp] = 0xff; } // GB bios set this memory area to 0 // Fixes Pitman (J) title screen if (gbHardware & 0x1) { memset(&gbMemory[0x8000], 0x0, 0x2000); } // clean LineBuffer if (gbLineBuffer != NULL) memset(gbLineBuffer, 0, sizeof(gbLineBuffer)); // clean Pix if (pix != NULL) memset(pix, 0, sizeof(pix)); // clean Vram if (gbVram != NULL) memset(gbVram, 0, 0x4000); // clean Wram 2 // This kinda emulates the startup state of Wram on GBC (not very accurate, // but way closer to the reality than filling it with 00es or FFes). // On GBA/GBASP, it's kinda filled with random data. // In all cases, most of the 2nd bank is filled with 00s. // The starting data are important for some 'buggy' games, like Buster Brothers or // Karamuchou ha Oosawagi! if (gbWram != NULL) { for (int i = 0; i<8; i++) if (i != 2) memcpy ((u16 *)(gbWram+i*0x1000), (u16 *)(gbMemory+0xC000), 0x1000); } memset(gbSCYLine,0,sizeof(gbSCYLine)); memset(gbSCXLine,0,sizeof(gbSCXLine)); memset(gbBgpLine,0xfc,sizeof(gbBgpLine)); if (gbHardware & 5) { memset(gbObp0Line,0xff,sizeof(gbObp0Line)); memset(gbObp1Line,0xff,sizeof(gbObp1Line)); } else { memset(gbObp0Line,0x0,sizeof(gbObp0Line)); memset(gbObp1Line,0x0,sizeof(gbObp1Line)); } memset(gbSpritesTicks,0x0,sizeof(gbSpritesTicks)); SP.W = 0xfffe; AF.W = 0x01b0; BC.W = 0x0013; DE.W = 0x00d8; HL.W = 0x014d; PC.W = 0x0100; IFF = 0; gbInt48Signal = 0; register_TIMA = 0; register_TMA = 0; register_TAC = 0; gbMemory[0xff0f] = register_IF = 1; gbMemory[0xff40] = register_LCDC = 0x91; gbMemory[0xff47] = 0xfc; if (gbCgbMode) gbMemory[0xff4d] = 0x7e; else gbMemory[0xff4d] = 0xff; if (!gbCgbMode) gbMemory[0xff70] = gbMemory[0xff74] = 0xff; if (gbCgbMode) gbMemory[0xff56] = 0x3e; else gbMemory[0xff56] = 0xff; register_SCY = 0; register_SCX = 0; register_LYC = 0; register_DMA = 0xff; register_WY = 0; register_WX = 0; register_VBK = 0; register_HDMA1 = 0xff; register_HDMA2 = 0xff; register_HDMA3 = 0xff; register_HDMA4 = 0xff; register_HDMA5 = 0xff; register_SVBK = 0; register_IE = 0; if (gbCgbMode) gbMemory[0xff02] = 0x7c; else gbMemory[0xff02] = 0x7e; gbMemory[0xff03] = 0xff; int i; for (i = 0x8; i<0xf; i++) gbMemory[0xff00+i] = 0xff; gbMemory[0xff13] = 0xff; gbMemory[0xff15] = 0xff; gbMemory[0xff18] = 0xff; gbMemory[0xff1d] = 0xff; gbMemory[0xff1f] = 0xff; for (i = 0x27; i<0x30; i++) gbMemory[0xff00+i] = 0xff; gbMemory[0xff4c] = 0xff; gbMemory[0xff4e] = 0xff; gbMemory[0xff50] = 0xff; for (i = 0x57; i<0x68; i++) gbMemory[0xff00+i] = 0xff; for (i = 0x5d; i<0x70; i++) gbMemory[0xff00+i] = 0xff; gbMemory[0xff71] = 0xff; for (i = 0x78; i<0x80; i++) gbMemory[0xff00+i] = 0xff; if (gbHardware & 0xa) { if (gbHardware & 2) { AF.W = 0x1180; BC.W = 0x0000; } else { AF.W = 0x1100; BC.W = 0x0100; // GBA/SP have B = 0x01 (which means GBC & GBA/SP bootrom are different !) } gbMemory[0xff26] = 0xf1; if (gbCgbMode) { gbMemory[0xff31] = 0xff; gbMemory[0xff33] = 0xff; gbMemory[0xff35] = 0xff; gbMemory[0xff37] = 0xff; gbMemory[0xff39] = 0xff; gbMemory[0xff3b] = 0xff; gbMemory[0xff3d] = 0xff; gbMemory[0xff44] = register_LY = 0x90; gbDivTicks = 0x19 + ((gbHardware & 2) >> 1); gbInternalTimer = 0x58 + ((gbHardware & 2) >> 1); gbLcdTicks = GBLCD_MODE_1_CLOCK_TICKS - (register_LY-0x8F)*GBLY_INCREMENT_CLOCK_TICKS + 72 + ((gbHardware & 2) >> 1); gbLcdLYIncrementTicks = 72 + ((gbHardware & 2) >> 1); gbMemory[0xff04] = register_DIV = 0x1E; } else { gbMemory[0xff44] = register_LY = 0x94; gbDivTicks = 0x22 + ((gbHardware & 2) >> 1); gbInternalTimer = 0x61 + ((gbHardware & 2) >> 1); gbLcdTicks = GBLCD_MODE_1_CLOCK_TICKS - (register_LY-0x8F)*GBLY_INCREMENT_CLOCK_TICKS + 25 + ((gbHardware & 2) >> 1); gbLcdLYIncrementTicks = 25 + ((gbHardware & 2) >> 1); gbMemory[0xff04] = register_DIV = 0x26; } DE.W = 0xff56; HL.W = 0x000d; register_HDMA5 = 0xff; gbMemory[0xff68] = 0xc0; gbMemory[0xff6a] = 0xc0; gbMemory[0xff41] = register_STAT = 0x81; gbLcdMode = 1; } else { if (gbHardware & 4) { if(gbEmulatorType == 5) AF.W = 0xffb0; else AF.W = 0x01b0; BC.W = 0x0013; DE.W = 0x00d8; HL.W = 0x014d; } gbDivTicks = 14; gbInternalTimer = gbDivTicks--; gbMemory[0xff04] = register_DIV = 0xAB; gbMemory[0xff41] = register_STAT = 0x85; gbMemory[0xff44] = register_LY = 0x00; gbLcdTicks = 15; gbLcdLYIncrementTicks = 114+gbLcdTicks; gbLcdMode = 1; // used for the handling of the gb Boot Rom if ((gbHardware & 5) && (bios != NULL) && useBios && !skipBios) { memcpy ((u8 *)(gbMemory), (u8 *)(gbRom), 0x1000); memcpy ((u8 *)(gbMemory), (u8 *)(bios), 0x100); gbWhiteScreen = 0; gbInternalTimer = 0x3e; gbDivTicks = 0x3f; gbMemory[0xff04] = register_DIV = 0x00; PC.W = 0x0000; register_LCDC = 0x11; gbScreenOn = false; gbLcdTicks = 0; gbLcdMode = 0; gbLcdModeDelayed = 0; gbMemory[0xff41] = register_STAT &= 0xfc; gbInt48Signal = 0; gbLcdLYIncrementTicks = GBLY_INCREMENT_CLOCK_TICKS; } } gbLine99Ticks = 1; if (gbHardware & 8) gbLine99Ticks++; gbLcdModeDelayed = gbLcdMode; gbLcdTicksDelayed = gbLcdTicks+1; gbLcdLYIncrementTicksDelayed = gbLcdLYIncrementTicks+1; gbTimerModeChange = false; gbTimerOnChange = false; gbTimerOn = false; if(gbCgbMode) { for (int i = 0; i<0x20; i++) gbPalette[i] = 0x7fff; // This is just to show that the starting values of the OBJ palettes are different // between the 3 consoles, and that they 'kinda' stay the same at each reset // (they can slightly change, somehow (randomly?)). // You can check the effects of gbGBCColorType on the "Vila Caldan Color" gbc demo. // Note that you could also check the Div register to check on which system the game // is running (GB,GBC and GBA(SP) have different startup values). // Unfortunatly, I don't have any SGB system, so I can't get their starting values. if (gbGBCColorType == 0) // GBC Hardware { gbPalette[0x20] = 0x0600; gbPalette[0x21] = 0xfdf3; gbPalette[0x22] = 0x041c; gbPalette[0x23] = 0xf5db; gbPalette[0x24] = 0x4419; gbPalette[0x25] = 0x57ea; gbPalette[0x26] = 0x2808; gbPalette[0x27] = 0x9b75; gbPalette[0x28] = 0x129b; gbPalette[0x29] = 0xfce0; gbPalette[0x2a] = 0x22da; gbPalette[0x2b] = 0x4ac5; gbPalette[0x2c] = 0x2d71; gbPalette[0x2d] = 0xf0c2; gbPalette[0x2e] = 0x5137; gbPalette[0x2f] = 0x2d41; gbPalette[0x30] = 0x6b2d; gbPalette[0x31] = 0x2215; gbPalette[0x32] = 0xbe0a; gbPalette[0x33] = 0xc053; gbPalette[0x34] = 0xfe5f; gbPalette[0x35] = 0xe000; gbPalette[0x36] = 0xbe10; gbPalette[0x37] = 0x914d; gbPalette[0x38] = 0x7f91; gbPalette[0x39] = 0x02b5; gbPalette[0x3a] = 0x77ac; gbPalette[0x3b] = 0x14e5; gbPalette[0x3c] = 0xcf89; gbPalette[0x3d] = 0xa03d; gbPalette[0x3e] = 0xfd50; gbPalette[0x3f] = 0x91ff; } else if (gbGBCColorType == 1) // GBA Hardware { gbPalette[0x20] = 0xbe00; gbPalette[0x21] = 0xfdfd; gbPalette[0x22] = 0xbd69; gbPalette[0x23] = 0x7baf; gbPalette[0x24] = 0xf5ff; gbPalette[0x25] = 0x3f8f; gbPalette[0x26] = 0xcee5; gbPalette[0x27] = 0x5bf7; gbPalette[0x28] = 0xb35b; gbPalette[0x29] = 0xef97; gbPalette[0x2a] = 0xef9f; gbPalette[0x2b] = 0x97f7; gbPalette[0x2c] = 0x82bf; gbPalette[0x2d] = 0x9f3d; gbPalette[0x2e] = 0xddde; gbPalette[0x2f] = 0xbad5; gbPalette[0x30] = 0x3cba; gbPalette[0x31] = 0xdfd7; gbPalette[0x32] = 0xedea; gbPalette[0x33] = 0xfeda; gbPalette[0x34] = 0xf7f9; gbPalette[0x35] = 0xfdee; gbPalette[0x36] = 0x6d2f; gbPalette[0x37] = 0xf0e6; gbPalette[0x38] = 0xf7f0; gbPalette[0x39] = 0xf296; gbPalette[0x3a] = 0x3bf1; gbPalette[0x3b] = 0xe211; gbPalette[0x3c] = 0x69ba; gbPalette[0x3d] = 0x3d0d; gbPalette[0x3e] = 0xdfd3; gbPalette[0x3f] = 0xa6ba; } else if (gbGBCColorType == 2) // GBASP Hardware { gbPalette[0x20] = 0x9c00; gbPalette[0x21] = 0x6340; gbPalette[0x22] = 0x10c6; gbPalette[0x23] = 0xdb97; gbPalette[0x24] = 0x7622; gbPalette[0x25] = 0x3e57; gbPalette[0x26] = 0x2e12; gbPalette[0x27] = 0x95c3; gbPalette[0x28] = 0x1095; gbPalette[0x29] = 0x488c; gbPalette[0x2a] = 0x8241; gbPalette[0x2b] = 0xde8c; gbPalette[0x2c] = 0xfabc; gbPalette[0x2d] = 0x0e81; gbPalette[0x2e] = 0x7675; gbPalette[0x2f] = 0xfdec; gbPalette[0x30] = 0xddfd; gbPalette[0x31] = 0x5995; gbPalette[0x32] = 0x066a; gbPalette[0x33] = 0xed1e; gbPalette[0x34] = 0x1e84; gbPalette[0x35] = 0x1d14; gbPalette[0x36] = 0x11c3; gbPalette[0x37] = 0x2749; gbPalette[0x38] = 0xa727; gbPalette[0x39] = 0x6266; gbPalette[0x3a] = 0xe27b; gbPalette[0x3b] = 0xe3fc; gbPalette[0x3c] = 0x1f76; gbPalette[0x3d] = 0xf158; gbPalette[0x3e] = 0x468e; gbPalette[0x3f] = 0xa540; } } else { if(gbSgbMode) { for(int i = 0; i < 8; i++) gbPalette[i] = systemGbPalette[gbPaletteOption*8+i]; } for(int i = 0; i < 8; i++) gbPalette[i] = systemGbPalette[gbPaletteOption*8+i]; } GBTIMER_MODE_0_CLOCK_TICKS = 256; GBTIMER_MODE_1_CLOCK_TICKS = 4; GBTIMER_MODE_2_CLOCK_TICKS = 16; GBTIMER_MODE_3_CLOCK_TICKS = 64; GBLY_INCREMENT_CLOCK_TICKS = 114; gbTimerTicks = GBTIMER_MODE_0_CLOCK_TICKS; gbTimerClockTicks = GBTIMER_MODE_0_CLOCK_TICKS; gbSerialTicks = 0; gbSerialBits = 0; gbSerialOn = 0; gbWindowLine = -1; gbTimerOn = false; gbTimerMode = 0; gbSpeed = 0; gbJoymask[0] = gbJoymask[1] = gbJoymask[2] = gbJoymask[3] = 0; if(gbCgbMode) { gbSpeed = 0; gbHdmaOn = 0; gbHdmaSource = 0x99d0; gbHdmaDestination = 0x99d0; gbVramBank = 0; gbWramBank = 1; } // used to clean the borders if (gbSgbMode) { gbSgbResetFlag = true; gbSgbReset(); if (gbBorderOn) gbSgbRenderBorder(); gbSgbResetFlag = false; } for(i = 0; i < 4; i++) gbBgp[i] = gbObp0[i] = gbObp1[i] = i; memset(&gbDataMBC1,0, sizeof(gbDataMBC1)); gbDataMBC1.mapperROMBank = 1; gbDataMBC2.mapperRAMEnable = 0; gbDataMBC2.mapperROMBank = 1; memset(&gbDataMBC3,0, 6 * sizeof(int)); gbDataMBC3.mapperROMBank = 1; memset(&gbDataMBC5, 0, sizeof(gbDataMBC5)); gbDataMBC5.mapperROMBank = 1; memset(&gbDataHuC1, 0, sizeof(gbDataHuC1)); gbDataHuC1.mapperROMBank = 1; memset(&gbDataHuC3, 0, sizeof(gbDataHuC3)); gbDataHuC3.mapperROMBank = 1; memset(&gbDataTAMA5,0, 26*sizeof(int)); gbDataTAMA5.mapperROMBank = 1; memset(&gbDataMMM01,0, sizeof(gbDataMMM01)); gbDataMMM01.mapperROMBank = 1; if (useBios && !skipBios && (gbHardware & 5)) { gbMemoryMap[0x00] = &gbMemory[0x0000]; inBios = true; } else { gbMemoryMap[0x00] = &gbRom[0x0000]; inBios = false; } gbMemoryMap[0x01] = &gbRom[0x1000]; gbMemoryMap[0x02] = &gbRom[0x2000]; gbMemoryMap[0x03] = &gbRom[0x3000]; gbMemoryMap[0x04] = &gbRom[0x4000]; gbMemoryMap[0x05] = &gbRom[0x5000]; gbMemoryMap[0x06] = &gbRom[0x6000]; gbMemoryMap[0x07] = &gbRom[0x7000]; if(gbCgbMode) { gbMemoryMap[0x08] = &gbVram[0x0000]; gbMemoryMap[0x09] = &gbVram[0x1000]; gbMemoryMap[0x0a] = &gbMemory[0xa000]; gbMemoryMap[0x0b] = &gbMemory[0xb000]; gbMemoryMap[0x0c] = &gbMemory[0xc000]; gbMemoryMap[0x0d] = &gbWram[0x1000]; gbMemoryMap[0x0e] = &gbMemory[0xe000]; gbMemoryMap[0x0f] = &gbMemory[0xf000]; } else { gbMemoryMap[0x08] = &gbMemory[0x8000]; gbMemoryMap[0x09] = &gbMemory[0x9000]; gbMemoryMap[0x0a] = &gbMemory[0xa000]; gbMemoryMap[0x0b] = &gbMemory[0xb000]; gbMemoryMap[0x0c] = &gbMemory[0xc000]; gbMemoryMap[0x0d] = &gbMemory[0xd000]; gbMemoryMap[0x0e] = &gbMemory[0xe000]; gbMemoryMap[0x0f] = &gbMemory[0xf000]; } if(gbRam) { gbMemoryMap[0x0a] = &gbRam[0x0000]; gbMemoryMap[0x0b] = &gbRam[0x1000]; } gbSoundReset(); systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; gbLastTime = systemGetClock(); gbFrameCount = 0; gbScreenOn = true; gbSystemMessage = false; gbCheatWrite(true); // Emulates GS codes. } void gbWriteSaveMBC1(const char * name) { if (gbRam) { FILE *gzFile = fopen(name,"wb"); if(gzFile == NULL) { systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); return; } fwrite(gbRam, 1, (gbRamSizeMask+1), gzFile); fclose(gzFile); } } void gbWriteSaveMBC2(const char * name) { if (gbRam) { FILE *file = fopen(name, "wb"); if(file == NULL) { systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); return; } fwrite(&gbMemory[0xa000], 1, 256, file); fclose(file); } } void gbWriteSaveMBC3(const char * name, bool extendedSave) { if (gbRam || extendedSave) { FILE *gzFile = fopen(name,"wb"); if (gbRam) { if(gzFile == NULL) { systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); return; } fwrite(gbRam, 1, (gbRamSizeMask+1), gzFile); } if(extendedSave) fwrite(&gbDataMBC3.mapperSeconds, 1, 10*sizeof(int) + sizeof(time_t), gzFile); fclose(gzFile); } } void gbWriteSaveMBC5(const char * name) { if (gbRam) { FILE *gzFile = fopen(name,"wb"); if(gzFile == NULL) { systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); return; } fwrite(gbRam, 1, (gbRamSizeMask+1), gzFile); fclose(gzFile); } } void gbWriteSaveMBC7(const char * name) { if (gbRam) { FILE *file = fopen(name, "wb"); if(file == NULL) { systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); return; } fwrite(&gbMemory[0xa000], 1, 256, file); fclose(file); } } void gbWriteSaveTAMA5(const char * name, bool extendedSave) { FILE *gzFile = fopen(name,"wb"); if(gzFile == NULL) { systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); return; } if (gbRam) fwrite(gbRam, 1, (gbRamSizeMask+1), gzFile); fwrite(gbTAMA5ram, 1, (gbTAMA5ramSize), gzFile); if(extendedSave) fwrite(&gbDataTAMA5.mapperSeconds, 1, 14*sizeof(int) + sizeof(time_t), gzFile); fclose(gzFile); } void gbWriteSaveMMM01(const char * name) { if (gbRam) { FILE *gzFile = fopen(name,"wb"); if(gzFile == NULL) { systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); return; } fwrite(gbRam, 1, (gbRamSizeMask+1), gzFile); fclose(gzFile); } } bool gbReadSaveMBC1(const char * name) { if (gbRam) { gzFile gzFile = gzopen(name, "rb"); if(gzFile == NULL) { return false; } int read = gzread(gzFile, gbRam, (gbRamSizeMask+1)); if(read != (gbRamSizeMask+1)) { systemMessage(MSG_FAILED_TO_READ_SGM, N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); gzclose(gzFile); gbBatteryError = true; return false; } // Also checks if the battery file it bigger than gbRamSizeMask+1 ! u8 data[1]; data[0] = 0; read = gzread(gzFile, data, 1); if(read >0) { systemMessage(MSG_FAILED_TO_READ_SGM, N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); gzclose(gzFile); gbBatteryError = true; return false; } gzclose(gzFile); return true; } else return false; } bool gbReadSaveMBC2(const char * name) { if (gbRam) { FILE *file = fopen(name, "rb"); if(file == NULL) { return false; } size_t read = fread(&gbMemory[0xa000], 1, 256, file); if(read != 256) { systemMessage(MSG_FAILED_TO_READ_SGM, N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); fclose(file); gbBatteryError = true; return false; } // Also checks if the battery file it bigger than gbRamSizeMask+1 ! u8 data[1]; data[0] = 0; read = fread(&data[0], 1, 1, file); if(read > 0) { systemMessage(MSG_FAILED_TO_READ_SGM, N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); fclose(file); gbBatteryError = true; return false; } fclose(file); return true; } else return false; } bool gbReadSaveMBC3(const char * name) { gzFile gzFile = gzopen(name, "rb"); if(gzFile == NULL) { return false; } int read = 0; if (gbRam) read = gzread(gzFile, gbRam, (gbRamSizeMask+1)); else read = (gbRamSizeMask+1); bool res = true; if(read != (gbRamSizeMask+1)) { systemMessage(MSG_FAILED_TO_READ_SGM, N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); gbBatteryError = true; res = false; } else if ((gbRomType == 0xf) || (gbRomType == 0x10)){ read = gzread(gzFile, &gbDataMBC3.mapperSeconds, sizeof(int)*10 + sizeof(time_t)); if(read != (sizeof(int)*10 + sizeof(time_t)) && read != 0) { systemMessage(MSG_FAILED_TO_READ_RTC,N_("Failed to read RTC from save game %s (continuing)"), name); res = false; } else if (read == 0) { systemMessage(MSG_FAILED_TO_READ_RTC,N_("Failed to read RTC from save game %s (continuing)"), name); res = false; } else { // Also checks if the battery file it bigger than gbRamSizeMask+1+RTC ! u8 data[1]; data[0] = 0; read = gzread(gzFile, data, 1); if(read >0) { systemMessage(MSG_FAILED_TO_READ_SGM, N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); gbBatteryError = true; res = false; } } } gzclose(gzFile); return res; } bool gbReadSaveMBC5(const char * name) { if (gbRam) { gzFile gzFile = gzopen(name, "rb"); if(gzFile == NULL) { return false; } int read = gzread(gzFile, gbRam, (gbRamSizeMask+1)); if(read != (gbRamSizeMask+1)) { systemMessage(MSG_FAILED_TO_READ_SGM, N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); gzclose(gzFile); gbBatteryError = true; return false; } // Also checks if the battery file it bigger than gbRamSizeMask+1 ! u8 data[1]; data[0] = 0; read = gzread(gzFile, data, 1); if(read >0) { systemMessage(MSG_FAILED_TO_READ_SGM, N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); gzclose(gzFile); gbBatteryError = true; return false; } gzclose(gzFile); return true; } else return false; } bool gbReadSaveMBC7(const char * name) { if (gbRam) { FILE *file = fopen(name, "rb"); if(file == NULL) { return false; } size_t read = fread(&gbMemory[0xa000], 1, 256, file); if(read != 256) { systemMessage(MSG_FAILED_TO_READ_SGM, N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); fclose(file); gbBatteryError = true; return false; } // Also checks if the battery file it bigger than gbRamSizeMask+1 ! u8 data[1]; data[0] = 0; read = fread(&data[0], 1, 1, file); if(read > 0) { systemMessage(MSG_FAILED_TO_READ_SGM, N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); fclose(file); gbBatteryError = true; return false; } fclose(file); return true; } else return false; } bool gbReadSaveTAMA5(const char * name) { gzFile gzFile = gzopen(name, "rb"); if(gzFile == NULL) { return false; } int read = 0; if (gbRam) read = gzread(gzFile, gbRam, (gbRamSizeMask+1)); else read = gbRamSizeMask; read += gzread(gzFile, gbTAMA5ram, gbTAMA5ramSize); bool res = true; if(read != (gbRamSizeMask+gbTAMA5ramSize+1)) { systemMessage(MSG_FAILED_TO_READ_SGM, N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); gbBatteryError = true; res = false; } else { read = gzread(gzFile, &gbDataTAMA5.mapperSeconds, sizeof(int)*14 + sizeof(time_t)); if(read != (sizeof(int)*14 + sizeof(time_t)) && read != 0) { systemMessage(MSG_FAILED_TO_READ_RTC,N_("Failed to read RTC from save game %s (continuing)"), name); res = false; } else if (read == 0) { systemMessage(MSG_FAILED_TO_READ_RTC,N_("Failed to read RTC from save game %s (continuing)"), name); res = false; } else { // Also checks if the battery file it bigger than gbRamSizeMask+1+RTC ! u8 data[1]; data[0] = 0; read = gzread(gzFile, data, 1); if(read >0) { systemMessage(MSG_FAILED_TO_READ_SGM, N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); gbBatteryError = true; res = false; } } } gzclose(gzFile); return res; } bool gbReadSaveMMM01(const char * name) { if (gbRam) { gzFile gzFile = gzopen(name, "rb"); if(gzFile == NULL) { return false; } int read = gzread(gzFile, gbRam, (gbRamSizeMask+1)); if(read != (gbRamSizeMask+1)) { systemMessage(MSG_FAILED_TO_READ_SGM, N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); gzclose(gzFile); gbBatteryError = true; return false; } // Also checks if the battery file it bigger than gbRamSizeMask+1 ! u8 data[1]; data[0] = 0; read = gzread(gzFile, data, 1); if(read >0) { systemMessage(MSG_FAILED_TO_READ_SGM, N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); gzclose(gzFile); gbBatteryError = true; return false; } gzclose(gzFile); return true; } else return false; } void gbInit() { gbGenFilter(); gbSgbInit(); gbMemory = (u8 *)malloc(65536); pix = (u8 *)calloc(1,4*257*226); gbLineBuffer = (u16 *)malloc(160 * sizeof(u16)); } bool gbWriteBatteryFile(const char *file, bool extendedSave) { if(gbBattery) { switch(gbRomType) { case 0x03: gbWriteSaveMBC1(file); break; case 0x06: gbWriteSaveMBC2(file); break; case 0x0d: gbWriteSaveMMM01(file); break; case 0x0f: case 0x10: gbWriteSaveMBC3(file, extendedSave); break; case 0x13: case 0xfc: gbWriteSaveMBC3(file, false); case 0x1b: case 0x1e: gbWriteSaveMBC5(file); break; case 0x22: gbWriteSaveMBC7(file); break; case 0xfd: gbWriteSaveTAMA5(file, extendedSave); break; case 0xff: gbWriteSaveMBC1(file); break; } } return true; } bool gbWriteBatteryFile(const char *file) { if (!gbBatteryError) { gbWriteBatteryFile(file, true); return true; } else return false; } bool gbReadBatteryFile(const char *file) { bool res = false; if(gbBattery) { switch(gbRomType) { case 0x03: res = gbReadSaveMBC1(file); break; case 0x06: res = gbReadSaveMBC2(file); break; case 0x0d: res = gbReadSaveMMM01(file); break; case 0x0f: case 0x10: if(!gbReadSaveMBC3(file)) { time(&gbDataMBC3.mapperLastTime); struct tm *lt; lt = localtime(&gbDataMBC3.mapperLastTime); gbDataMBC3.mapperSeconds = lt->tm_sec; gbDataMBC3.mapperMinutes = lt->tm_min; gbDataMBC3.mapperHours = lt->tm_hour; gbDataMBC3.mapperDays = lt->tm_yday & 255; gbDataMBC3.mapperControl = (gbDataMBC3.mapperControl & 0xfe) | (lt->tm_yday > 255 ? 1: 0); res = false; break; } res = true; break; case 0x13: case 0xfc: res = gbReadSaveMBC3(file); break; case 0x1b: case 0x1e: res = gbReadSaveMBC5(file); break; case 0x22: res = gbReadSaveMBC7(file); break; case 0xfd: if(!gbReadSaveTAMA5(file)) { u8 gbDaysinMonth [12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; time(&gbDataTAMA5.mapperLastTime); struct tm *lt; lt = localtime(&gbDataTAMA5.mapperLastTime); gbDataTAMA5.mapperSeconds = lt->tm_sec; gbDataTAMA5.mapperMinutes = lt->tm_min; gbDataTAMA5.mapperHours = lt->tm_hour; gbDataTAMA5.mapperDays = 1; gbDataTAMA5.mapperMonths = 1; gbDataTAMA5.mapperYears = 1970; int days = lt->tm_yday+365*3; while (days) { gbDataTAMA5.mapperDays++; days--; if (gbDataTAMA5.mapperDays>gbDaysinMonth[gbDataTAMA5.mapperMonths-1]) { gbDataTAMA5.mapperDays = 1; gbDataTAMA5.mapperMonths++; if (gbDataTAMA5.mapperMonths>12) { gbDataTAMA5.mapperMonths = 1; gbDataTAMA5.mapperYears++; if ((gbDataTAMA5.mapperYears & 3) == 0) gbDaysinMonth[1] = 29; else gbDaysinMonth[1] = 28; } } } gbDataTAMA5.mapperControl = (gbDataTAMA5.mapperControl & 0xfe) | (lt->tm_yday > 255 ? 1: 0); res = false; break; } res = true; break; case 0xff: res = gbReadSaveMBC1(file); break; } } systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; return res; } bool gbReadGSASnapshot(const char *fileName) { FILE *file = fopen(fileName, "rb"); if(!file) { systemMessage(MSG_CANNOT_OPEN_FILE, N_("Cannot open file %s"), fileName); return false; } fseek(file, 0x4, SEEK_SET); char buffer[16]; char buffer2[16]; fread(buffer, 1, 15, file); buffer[15] = 0; memcpy(buffer2, &gbRom[0x134], 15); buffer2[15] = 0; if(memcmp(buffer, buffer2, 15)) { systemMessage(MSG_CANNOT_IMPORT_SNAPSHOT_FOR, N_("Cannot import snapshot for %s. Current game is %s"), buffer, buffer2); fclose(file); return false; } fseek(file, 0x13, SEEK_SET); size_t read = 0; int toRead = 0; switch(gbRomType) { case 0x03: case 0x0f: case 0x10: case 0x13: case 0x1b: case 0x1e: case 0xff: read = fread(gbRam, 1, (gbRamSizeMask+1), file); toRead = (gbRamSizeMask+1); break; case 0x06: case 0x22: read = fread(&gbMemory[0xa000],1,256,file); toRead = 256; break; default: systemMessage(MSG_UNSUPPORTED_SNAPSHOT_FILE, N_("Unsupported snapshot file %s"), fileName); fclose(file); return false; } fclose(file); gbReset(); return true; } variable_desc gbSaveGameStruct[] = { { &PC.W, sizeof(u16) }, { &SP.W, sizeof(u16) }, { &AF.W, sizeof(u16) }, { &BC.W, sizeof(u16) }, { &DE.W, sizeof(u16) }, { &HL.W, sizeof(u16) }, { &IFF, sizeof(u8) }, { &GBLCD_MODE_0_CLOCK_TICKS, sizeof(int) }, { &GBLCD_MODE_1_CLOCK_TICKS, sizeof(int) }, { &GBLCD_MODE_2_CLOCK_TICKS, sizeof(int) }, { &GBLCD_MODE_3_CLOCK_TICKS, sizeof(int) }, { &GBDIV_CLOCK_TICKS, sizeof(int) }, { &GBLY_INCREMENT_CLOCK_TICKS, sizeof(int) }, { &GBTIMER_MODE_0_CLOCK_TICKS, sizeof(int) }, { &GBTIMER_MODE_1_CLOCK_TICKS, sizeof(int) }, { &GBTIMER_MODE_2_CLOCK_TICKS, sizeof(int) }, { &GBTIMER_MODE_3_CLOCK_TICKS, sizeof(int) }, { &GBSERIAL_CLOCK_TICKS, sizeof(int) }, { &GBSYNCHRONIZE_CLOCK_TICKS, sizeof(int) }, { &gbDivTicks, sizeof(int) }, { &gbLcdMode, sizeof(int) }, { &gbLcdTicks, sizeof(int) }, { &gbLcdLYIncrementTicks, sizeof(int) }, { &gbTimerTicks, sizeof(int) }, { &gbTimerClockTicks, sizeof(int) }, { &gbSerialTicks, sizeof(int) }, { &gbSerialBits, sizeof(int) }, { &gbInt48Signal, sizeof(int) }, { &gbInterruptWait, sizeof(int) }, { &gbSynchronizeTicks, sizeof(int) }, { &gbTimerOn, sizeof(int) }, { &gbTimerMode, sizeof(int) }, { &gbSerialOn, sizeof(int) }, { &gbWindowLine, sizeof(int) }, { &gbCgbMode, sizeof(int) }, { &gbVramBank, sizeof(int) }, { &gbWramBank, sizeof(int) }, { &gbHdmaSource, sizeof(int) }, { &gbHdmaDestination, sizeof(int) }, { &gbHdmaBytes, sizeof(int) }, { &gbHdmaOn, sizeof(int) }, { &gbSpeed, sizeof(int) }, { &gbSgbMode, sizeof(int) }, { ®ister_DIV, sizeof(u8) }, { ®ister_TIMA, sizeof(u8) }, { ®ister_TMA, sizeof(u8) }, { ®ister_TAC, sizeof(u8) }, { ®ister_IF, sizeof(u8) }, { ®ister_LCDC, sizeof(u8) }, { ®ister_STAT, sizeof(u8) }, { ®ister_SCY, sizeof(u8) }, { ®ister_SCX, sizeof(u8) }, { ®ister_LY, sizeof(u8) }, { ®ister_LYC, sizeof(u8) }, { ®ister_DMA, sizeof(u8) }, { ®ister_WY, sizeof(u8) }, { ®ister_WX, sizeof(u8) }, { ®ister_VBK, sizeof(u8) }, { ®ister_HDMA1, sizeof(u8) }, { ®ister_HDMA2, sizeof(u8) }, { ®ister_HDMA3, sizeof(u8) }, { ®ister_HDMA4, sizeof(u8) }, { ®ister_HDMA5, sizeof(u8) }, { ®ister_SVBK, sizeof(u8) }, { ®ister_IE , sizeof(u8) }, { &gbBgp[0], sizeof(u8) }, { &gbBgp[1], sizeof(u8) }, { &gbBgp[2], sizeof(u8) }, { &gbBgp[3], sizeof(u8) }, { &gbObp0[0], sizeof(u8) }, { &gbObp0[1], sizeof(u8) }, { &gbObp0[2], sizeof(u8) }, { &gbObp0[3], sizeof(u8) }, { &gbObp1[0], sizeof(u8) }, { &gbObp1[1], sizeof(u8) }, { &gbObp1[2], sizeof(u8) }, { &gbObp1[3], sizeof(u8) }, { NULL, 0 } }; static bool gbWriteSaveState(gzFile gzFile) { utilWriteInt(gzFile, GBSAVE_GAME_VERSION); utilGzWrite(gzFile, &gbRom[0x134], 15); utilWriteInt(gzFile, useBios); utilWriteInt(gzFile, inBios); utilWriteData(gzFile, gbSaveGameStruct); utilGzWrite(gzFile, &IFF, 2); if(gbSgbMode) { gbSgbSaveGame(gzFile); } utilGzWrite(gzFile, &gbDataMBC1, sizeof(gbDataMBC1)); utilGzWrite(gzFile, &gbDataMBC2, sizeof(gbDataMBC2)); utilGzWrite(gzFile, &gbDataMBC3, sizeof(gbDataMBC3)); utilGzWrite(gzFile, &gbDataMBC5, sizeof(gbDataMBC5)); utilGzWrite(gzFile, &gbDataHuC1, sizeof(gbDataHuC1)); utilGzWrite(gzFile, &gbDataHuC3, sizeof(gbDataHuC3)); utilGzWrite(gzFile, &gbDataTAMA5, sizeof(gbDataTAMA5)); if (gbTAMA5ram != NULL) utilGzWrite(gzFile, gbTAMA5ram, gbTAMA5ramSize); utilGzWrite(gzFile, &gbDataMMM01, sizeof(gbDataMMM01)); utilGzWrite(gzFile, gbPalette, 128 * sizeof(u16)); utilGzWrite(gzFile, &gbMemory[0x8000], 0x8000); if(gbRamSize && gbRam) { utilWriteInt(gzFile, gbRamSize); utilGzWrite(gzFile, gbRam, gbRamSize); } if(gbCgbMode) { utilGzWrite(gzFile, gbVram, 0x4000); utilGzWrite(gzFile, gbWram, 0x8000); } gbSoundSaveGame(gzFile); gbCheatsSaveGame(gzFile); utilWriteInt(gzFile, gbLcdModeDelayed); utilWriteInt(gzFile, gbLcdTicksDelayed); utilWriteInt(gzFile, gbLcdLYIncrementTicksDelayed); utilWriteInt(gzFile, gbSpritesTicks[299]); utilWriteInt(gzFile, gbTimerModeChange); utilWriteInt(gzFile, gbTimerOnChange); utilWriteInt(gzFile, gbHardware); utilWriteInt(gzFile, gbBlackScreen); utilWriteInt(gzFile, oldRegister_WY); utilWriteInt(gzFile, gbWindowLine); utilWriteInt(gzFile, inUseRegister_WY); utilWriteInt(gzFile, gbScreenOn); utilWriteInt(gzFile, 0x12345678); // end marker return true; } bool gbWriteMemSaveState(char *memory, int available) { gzFile gzFile = utilMemGzOpen(memory, available, "w"); if(gzFile == NULL) { return false; } bool res = gbWriteSaveState(gzFile); long pos = utilGzMemTell(gzFile)+8; if(pos >= (available)) res = false; utilGzClose(gzFile); return res; } bool gbWriteSaveState(const char *name) { gzFile gzFile = utilGzOpen(name,"wb"); if(gzFile == NULL) return false; bool res = gbWriteSaveState(gzFile); utilGzClose(gzFile); return res; } static bool gbReadSaveState(gzFile gzFile) { int version = utilReadInt(gzFile); if(version > GBSAVE_GAME_VERSION || version < 0) { systemMessage(MSG_UNSUPPORTED_VB_SGM, N_("Unsupported VisualBoy save game version %d"), version); return false; } u8 romname[20]; utilGzRead(gzFile, romname, 15); if(memcmp(&gbRom[0x134], romname, 15) != 0) { systemMessage(MSG_CANNOT_LOAD_SGM_FOR, N_("Cannot load save game for %s. Playing %s"), romname, &gbRom[0x134]); return false; } bool ub = false; bool ib = false; if (version >= 11) { ub = utilReadInt(gzFile) ? true : false; ib = utilReadInt(gzFile) ? true : false; if((ub != useBios) && (ib)) { if(useBios) systemMessage(MSG_SAVE_GAME_NOT_USING_BIOS, N_("Save game is not using the BIOS files")); else systemMessage(MSG_SAVE_GAME_USING_BIOS, N_("Save game is using the BIOS file")); return false; } } gbReset(); inBios = ib; utilReadData(gzFile, gbSaveGameStruct); // Correct crash when loading color gameboy save in regular gameboy type. if (!gbCgbMode) { if(gbVram != NULL) { free(gbVram); gbVram = NULL; } if(gbWram != NULL) { free(gbWram); gbWram = NULL; } } else { if(gbVram == NULL) gbVram = (u8 *)malloc(0x4000); if(gbWram == NULL) gbWram = (u8 *)malloc(0x8000); memset(gbVram,0,0x4000); memset(gbPalette,0, 2*128); } if(version >= GBSAVE_GAME_VERSION_7) { utilGzRead(gzFile, &IFF, 2); } if(gbSgbMode) { gbSgbReadGame(gzFile, version); } else { gbSgbMask = 0; // loading a game at the wrong time causes no display } if (version<11) utilGzRead(gzFile, &gbDataMBC1, sizeof(gbDataMBC1) - sizeof(int)); else utilGzRead(gzFile, &gbDataMBC1, sizeof(gbDataMBC1)); utilGzRead(gzFile, &gbDataMBC2, sizeof(gbDataMBC2)); if(version < GBSAVE_GAME_VERSION_4) // prior to version 4, there was no adjustment for the time the game // was last played, so we have less to read. This needs update if the // structure changes again. utilGzRead(gzFile, &gbDataMBC3, sizeof(gbDataMBC3)-sizeof(time_t)); else utilGzRead(gzFile, &gbDataMBC3, sizeof(gbDataMBC3)); utilGzRead(gzFile, &gbDataMBC5, sizeof(gbDataMBC5)); utilGzRead(gzFile, &gbDataHuC1, sizeof(gbDataHuC1)); utilGzRead(gzFile, &gbDataHuC3, sizeof(gbDataHuC3)); if(version>=11) { utilGzRead(gzFile, &gbDataTAMA5, sizeof(gbDataTAMA5)); if(gbTAMA5ram != NULL) { if(skipSaveGameBattery) { utilGzSeek(gzFile, gbTAMA5ramSize, SEEK_CUR); } else { utilGzRead(gzFile, gbTAMA5ram, gbTAMA5ramSize); } } utilGzRead(gzFile, &gbDataMMM01, sizeof(gbDataMMM01)); } if(version < GBSAVE_GAME_VERSION_5) { utilGzRead(gzFile, pix, 256*224*sizeof(u16)); } memset(pix, 0, 257*226*sizeof(u32)); if(version < GBSAVE_GAME_VERSION_6) { utilGzRead(gzFile, gbPalette, 64 * sizeof(u16)); } else utilGzRead(gzFile, gbPalette, 128 * sizeof(u16)); if (version < 11) utilGzRead(gzFile, gbPalette, 128 * sizeof(u16)); if(version < GBSAVE_GAME_VERSION_10) { if(!gbCgbMode && !gbSgbMode) { for(int i = 0; i < 8; i++) gbPalette[i] = systemGbPalette[gbPaletteOption*8+i]; } } utilGzRead(gzFile, &gbMemory[0x8000], 0x8000); if(gbRamSize && gbRam) { if(version < 11) if(skipSaveGameBattery) { utilGzSeek(gzFile, gbRamSize, SEEK_CUR); //skip } else { utilGzRead(gzFile, gbRam, gbRamSize); //read } else { int ramSize = utilReadInt(gzFile); if(skipSaveGameBattery) { utilGzSeek(gzFile, (gbRamSize>ramSize) ? ramSize : gbRamSize, SEEK_CUR); //skip } else { utilGzRead(gzFile, gbRam, (gbRamSize>ramSize) ? ramSize : gbRamSize); //read } if(ramSize>gbRamSize) utilGzSeek(gzFile,ramSize-gbRamSize,SEEK_CUR); } } memset(gbSCYLine, register_SCY, sizeof(gbSCYLine)); memset(gbSCXLine, register_SCX, sizeof(gbSCXLine)); memset(gbBgpLine, (gbBgp[0] | (gbBgp[1]<<2) | (gbBgp[2]<<4) | (gbBgp[3]<<6)), sizeof(gbBgpLine)); memset(gbObp0Line, (gbObp0[0] | (gbObp0[1]<<2) | (gbObp0[2]<<4) | (gbObp0[3]<<6)), sizeof(gbObp0Line)); memset(gbObp1Line, (gbObp1[0] | (gbObp1[1]<<2) | (gbObp1[2]<<4) | (gbObp1[3]<<6)), sizeof(gbObp1Line)); memset(gbSpritesTicks, 0x0, sizeof(gbSpritesTicks)); if (inBios) { gbMemoryMap[0x00] = &gbMemory[0x0000]; memcpy ((u8 *)(gbMemory), (u8 *)(gbRom), 0x1000); memcpy ((u8 *)(gbMemory), (u8 *)(bios), 0x100); } else gbMemoryMap[0x00] = &gbRom[0x0000]; gbMemoryMap[0x01] = &gbRom[0x1000]; gbMemoryMap[0x02] = &gbRom[0x2000]; gbMemoryMap[0x03] = &gbRom[0x3000]; gbMemoryMap[0x04] = &gbRom[0x4000]; gbMemoryMap[0x05] = &gbRom[0x5000]; gbMemoryMap[0x06] = &gbRom[0x6000]; gbMemoryMap[0x07] = &gbRom[0x7000]; gbMemoryMap[0x08] = &gbMemory[0x8000]; gbMemoryMap[0x09] = &gbMemory[0x9000]; gbMemoryMap[0x0a] = &gbMemory[0xa000]; gbMemoryMap[0x0b] = &gbMemory[0xb000]; gbMemoryMap[0x0c] = &gbMemory[0xc000]; gbMemoryMap[0x0d] = &gbMemory[0xd000]; gbMemoryMap[0x0e] = &gbMemory[0xe000]; gbMemoryMap[0x0f] = &gbMemory[0xf000]; switch(gbRomType) { case 0x00: case 0x01: case 0x02: case 0x03: // MBC 1 memoryUpdateMapMBC1(); break; case 0x05: case 0x06: // MBC2 memoryUpdateMapMBC2(); break; case 0x0b: case 0x0c: case 0x0d: // MMM01 memoryUpdateMapMMM01(); break; case 0x0f: case 0x10: case 0x11: case 0x12: case 0x13: // MBC 3 memoryUpdateMapMBC3(); break; case 0x19: case 0x1a: case 0x1b: // MBC5 memoryUpdateMapMBC5(); break; case 0x1c: case 0x1d: case 0x1e: // MBC 5 Rumble memoryUpdateMapMBC5(); break; case 0x22: // MBC 7 memoryUpdateMapMBC7(); break; case 0x56: // GS3 memoryUpdateMapGS3(); break; case 0xfd: // TAMA5 memoryUpdateMapTAMA5(); break; case 0xfe: // HuC3 memoryUpdateMapHuC3(); break; case 0xff: // HuC1 memoryUpdateMapHuC1(); break; } if(gbCgbMode) { utilGzRead(gzFile, gbVram, 0x4000); utilGzRead(gzFile, gbWram, 0x8000); int value = register_SVBK; if(value == 0) value = 1; gbMemoryMap[0x08] = &gbVram[register_VBK * 0x2000]; gbMemoryMap[0x09] = &gbVram[register_VBK * 0x2000 + 0x1000]; gbMemoryMap[0x0d] = &gbWram[value * 0x1000]; } gbSoundReadGame(version, gzFile); if (gbCgbMode && gbSgbMode) { gbSgbMode = 0; } if(gbBorderOn && !gbSgbMask) { gbSgbRenderBorder(); } systemDrawScreen(); if(version > GBSAVE_GAME_VERSION_1) { if( skipSaveGameCheats ) { gbCheatsReadGameSkip(gzFile, version); } else { gbCheatsReadGame(gzFile, version); } } if (version<11) { gbWriteMemory(0xff00, 0); gbMemory[0xff04] = register_DIV; gbMemory[0xff05] = register_TIMA; gbMemory[0xff06] = register_TMA; gbMemory[0xff07] = register_TAC; gbMemory[0xff40] = register_LCDC; gbMemory[0xff42] = register_SCY; gbMemory[0xff43] = register_SCX; gbMemory[0xff44] = register_LY; gbMemory[0xff45] = register_LYC; gbMemory[0xff46] = register_DMA; gbMemory[0xff4a] = register_WY; gbMemory[0xff4b] = register_WX; gbMemory[0xff4f] = register_VBK; gbMemory[0xff51] = register_HDMA1; gbMemory[0xff52] = register_HDMA2; gbMemory[0xff53] = register_HDMA3; gbMemory[0xff54] = register_HDMA4; gbMemory[0xff55] = register_HDMA5; gbMemory[0xff70] = register_SVBK; gbMemory[0xffff] = register_IE; GBDIV_CLOCK_TICKS = 64; if (gbSpeed) gbDivTicks /=2; if ((gbLcdMode == 0) && (register_STAT & 8)) gbInt48Signal |= 1; if ((gbLcdMode == 1) && (register_STAT & 0x10)) gbInt48Signal |= 2; if ((gbLcdMode == 2) && (register_STAT & 0x20)) gbInt48Signal |= 4; if ((register_LY==register_LYC) && (register_STAT & 0x40)) gbInt48Signal |= 8; gbLcdLYIncrementTicks = GBLY_INCREMENT_CLOCK_TICKS; if (gbLcdMode == 2) gbLcdLYIncrementTicks-=GBLCD_MODE_2_CLOCK_TICKS-gbLcdTicks; else if (gbLcdMode == 3) gbLcdLYIncrementTicks -=GBLCD_MODE_2_CLOCK_TICKS+GBLCD_MODE_3_CLOCK_TICKS-gbLcdTicks; else if (gbLcdMode == 0) gbLcdLYIncrementTicks =gbLcdTicks; else if (gbLcdMode == 1) { gbLcdLYIncrementTicks = gbLcdTicks % GBLY_INCREMENT_CLOCK_TICKS; if (register_LY == 0x99) gbLcdLYIncrementTicks =gbLine99Ticks; else if (register_LY == 0) gbLcdLYIncrementTicks += GBLY_INCREMENT_CLOCK_TICKS; } gbLcdModeDelayed = gbLcdMode; gbLcdTicksDelayed = gbLcdTicks--; gbLcdLYIncrementTicksDelayed = gbLcdLYIncrementTicks--; gbInterruptWait = 0; memset(gbSpritesTicks,0,sizeof(gbSpritesTicks)); } else { gbLcdModeDelayed = utilReadInt(gzFile); gbLcdTicksDelayed = utilReadInt(gzFile); gbLcdLYIncrementTicksDelayed = utilReadInt(gzFile); gbSpritesTicks[299] = utilReadInt(gzFile) & 0xff; gbTimerModeChange = (utilReadInt(gzFile) ? true : false); gbTimerOnChange = (utilReadInt(gzFile) ? true : false); gbHardware = utilReadInt(gzFile); gbBlackScreen = (utilReadInt(gzFile) ? true : false); oldRegister_WY = utilReadInt(gzFile); gbWindowLine = utilReadInt(gzFile); inUseRegister_WY = utilReadInt(gzFile); gbScreenOn = (utilReadInt(gzFile) ? true : false); } if (gbSpeed) gbLine99Ticks *= 2; systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; if ( version >= 12 && utilReadInt( gzFile ) != 0x12345678 ) assert( false ); // fails if something read too much/little from file return true; } bool gbReadMemSaveState(char *memory, int available) { gzFile gzFile = utilMemGzOpen(memory, available, "r"); bool res = gbReadSaveState(gzFile); utilGzClose(gzFile); return res; } bool gbReadSaveState(const char *name) { gzFile gzFile = utilGzOpen(name,"rb"); if(gzFile == NULL) { return false; } bool res = gbReadSaveState(gzFile); utilGzClose(gzFile); return res; } bool gbWritePNGFile(const char *fileName) { /* if(gbBorderOn) return utilWritePNGFile(fileName, 256, 224, pix); return utilWritePNGFile(fileName, 160, 144, pix);*/ return false; } bool gbWriteBMPFile(const char *fileName) { /* if(gbBorderOn) return utilWriteBMPFile(fileName, 256, 224, pix); return utilWriteBMPFile(fileName, 160, 144, pix);*/ return false; } void gbCleanUp() { if(gbRam != NULL) { free(gbRam); gbRam = NULL; } if(gbRom != NULL) { free(gbRom); gbRom = NULL; } if(bios != NULL) { free(bios); bios = NULL; } if(gbMemory != NULL) { free(gbMemory); gbMemory = NULL; } if(gbLineBuffer != NULL) { free(gbLineBuffer); gbLineBuffer = NULL; } if(pix != NULL) { free(pix); pix = NULL; } gbSgbShutdown(); if(gbVram != NULL) { free(gbVram); gbVram = NULL; } if(gbWram != NULL) { free(gbWram); gbWram = NULL; } if(gbTAMA5ram != NULL) { free(gbTAMA5ram); gbTAMA5ram = NULL; } systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; } bool gbLoadRom(const char *szFile) { int size = 0; if(gbRom != NULL) { gbCleanUp(); } systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; /* gbRom = utilLoad(szFile, utilIsGBImage, NULL, size);*/ if(!gbRom) return false; gbRomSize = size; gbBatteryError = false; if(bios != NULL) { free(bios); bios = NULL; } bios = (u8 *)calloc(1,0x100); return gbUpdateSizes(); } bool gbUpdateSizes() { if(gbRom[0x148] > 8) { systemMessage(MSG_UNSUPPORTED_ROM_SIZE, N_("Unsupported rom size %02x"), gbRom[0x148]); return false; } if(gbRomSize < gbRomSizes[gbRom[0x148]]) { gbRom = (u8 *)realloc(gbRom, gbRomSizes[gbRom[0x148]]); for (int i = gbRomSize; igbRomSizes[gbRom[0x148]]) && (genericflashcardEnable)) { gbRomSize = gbRomSize>>16; gbRom[0x148] = 0; if (gbRomSize) { while (!((gbRomSize & 1) || (gbRom[0x148] == 7))) { gbRom[0x148]++; gbRomSize>>=1; } gbRom[0x148]++; } gbRom = (u8 *)realloc(gbRom, gbRomSizes[gbRom[0x148]]); } gbRomSize = gbRomSizes[gbRom[0x148]]; gbRomSizeMask = gbRomSizesMasks[gbRom[0x148]]; // The 'genericflashcard' option allows some PD to work. // However, the setting is dangerous (if you let in enabled // and play a normal game, it might just break everything). // That's why it is not saved in the emulator options. // Also I added some checks in VBA to make sure your saves will not be // overwritten if you wrongly enable this option for a game // you already played (and vice-versa, ie. if you forgot to // enable the option for a game you played with it enabled, like Shawu Story). u8 ramsize = genericflashcardEnable ? 5 : gbRom[0x149]; gbRom[0x149] = ramsize; if ((gbRom[2] == 0x6D) && (gbRom[5] == 0x47) && (gbRom[6] == 0x65) && (gbRom[7] == 0x6E) && (gbRom[8] == 0x69) && (gbRom[9] == 0x65) && (gbRom[0xA] == 0x28) && (gbRom[0xB] == 0x54)) { gbCheatingDevice = 1; // GameGenie for (int i = 0; i < 0x20; i++) // Cleans GG hardware registers gbRom[0x4000+i] = 0; } else if (((gbRom[0x104] == 0x44) && (gbRom[0x156] == 0xEA) && (gbRom[0x158] == 0x7F) && (gbRom[0x159] == 0xEA) && (gbRom[0x15B] == 0x7F)) || ((gbRom[0x165] == 0x3E) && (gbRom[0x166] == 0xD9) && (gbRom[0x16D] == 0xE1) && (gbRom[0x16E] == 0x7F))) gbCheatingDevice = 2; // GameShark else gbCheatingDevice = 0; if(ramsize > 5) { systemMessage(MSG_UNSUPPORTED_RAM_SIZE, N_("Unsupported ram size %02x"), gbRom[0x149]); return false; } gbRamSize = gbRamSizes[ramsize]; gbRamSizeMask = gbRamSizesMasks[ramsize]; gbRomType = gbRom[0x147]; if (genericflashcardEnable) { /*if (gbRomType<2) gbRomType =3; else if ((gbRomType == 0xc) || (gbRomType == 0xf) || (gbRomType == 0x12) || (gbRomType == 0x16) || (gbRomType == 0x1a) || (gbRomType == 0x1d)) gbRomType++; else if ((gbRomType == 0xb) || (gbRomType == 0x11) || (gbRomType == 0x15) || (gbRomType == 0x19) || (gbRomType == 0x1c)) gbRomType+=2; else if ((gbRomType == 0x5) || (gbRomType == 0x6)) gbRomType = 0x1a;*/ gbRomType = 0x1b; } else if (gbCheatingDevice == 1) gbRomType = 0x55; else if (gbCheatingDevice == 2) gbRomType = 0x56; gbRom[0x147] = gbRomType; mapperReadRAM = NULL; switch(gbRomType) { case 0x00: case 0x01: case 0x02: case 0x03: case 0x08: case 0x09: // MBC 1 mapper = mapperMBC1ROM; mapperRAM = mapperMBC1RAM; mapperReadRAM = mapperMBC1ReadRAM; break; case 0x05: case 0x06: // MBC2 mapper = mapperMBC2ROM; mapperRAM = mapperMBC2RAM; gbRamSize = 0x200; gbRamSizeMask = 0x1ff; break; case 0x0b: case 0x0c: case 0x0d: // MMM01 mapper = mapperMMM01ROM; mapperRAM = mapperMMM01RAM; break; case 0x0f: case 0x10: case 0x11: case 0x12: case 0x13: case 0xfc: // MBC 3 mapper = mapperMBC3ROM; mapperRAM = mapperMBC3RAM; mapperReadRAM = mapperMBC3ReadRAM; break; case 0x19: case 0x1a: case 0x1b: // MBC5 mapper = mapperMBC5ROM; mapperRAM = mapperMBC5RAM; mapperReadRAM = mapperMBC5ReadRAM; break; case 0x1c: case 0x1d: case 0x1e: // MBC 5 Rumble mapper = mapperMBC5ROM; mapperRAM = mapperMBC5RAM; mapperReadRAM = mapperMBC5ReadRAM; break; case 0x22: // MBC 7 mapper = mapperMBC7ROM; mapperRAM = mapperMBC7RAM; mapperReadRAM = mapperMBC7ReadRAM; gbRamSize = 0x200; gbRamSizeMask = 0x1ff; break; // GG (GameGenie) case 0x55: mapper = mapperGGROM; break; case 0x56: // GS (GameShark) mapper = mapperGS3ROM; break; case 0xfd: // TAMA5 if (gbRam!= NULL) { free(gbRam); gbRam = NULL; } ramsize = 3; gbRamSize = gbRamSizes[3]; gbRamSizeMask = gbRamSizesMasks[3]; gbRamFill = 0x0; gbTAMA5ramSize = 0x100; if (gbTAMA5ram == NULL) gbTAMA5ram = (u8 *)malloc(gbTAMA5ramSize); memset(gbTAMA5ram, 0x0, gbTAMA5ramSize); mapperRAM = mapperTAMA5RAM; mapperReadRAM = mapperTAMA5ReadRAM; mapperUpdateClock = memoryUpdateTAMA5Clock; break; case 0xfe: // HuC3 mapper = mapperHuC3ROM; mapperRAM = mapperHuC3RAM; mapperReadRAM = mapperHuC3ReadRAM; break; case 0xff: // HuC1 mapper = mapperHuC1ROM; mapperRAM = mapperHuC1RAM; break; default: systemMessage(MSG_UNKNOWN_CARTRIDGE_TYPE, N_("Unknown cartridge type %02x"), gbRomType); return false; } if(gbRamSize) { gbRam = (u8 *)malloc(gbRamSize); memset(gbRam, gbRamFill, gbRamSize); } switch(gbRomType) { case 0x03: case 0x06: case 0x0f: case 0x10: case 0x13: case 0x1b: case 0x1d: case 0x1e: case 0x22: case 0xfd: case 0xff: gbBattery = 1; break; } gbInit(); //gbReset(); switch(gbRomType) { case 0x1c: case 0x1d: case 0x1e: gbDataMBC5.isRumbleCartridge = 1; } return true; } int gbGetNextEvent (int clockTicks) { if (register_LCDC & 0x80) { if(gbLcdTicks < clockTicks) clockTicks = gbLcdTicks; if(gbLcdTicksDelayed < clockTicks) clockTicks = gbLcdTicksDelayed; if(gbLcdLYIncrementTicksDelayed < clockTicks) clockTicks = gbLcdLYIncrementTicksDelayed; } if(gbLcdLYIncrementTicks < clockTicks) clockTicks = gbLcdLYIncrementTicks; if(gbSerialOn && (gbSerialTicks < clockTicks)) clockTicks = gbSerialTicks; if(gbTimerOn && (((gbInternalTimer) & gbTimerMask[gbTimerMode])+1 < clockTicks)) clockTicks = ((gbInternalTimer) & gbTimerMask[gbTimerMode])+1; //if(soundTicks && (soundTicks < clockTicks)) // clockTicks = soundTicks; if ((clockTicks<=0) || (gbInterruptWait)) clockTicks = 1; return clockTicks; } void gbDrawLine() { switch(systemColorDepth) { case 16: { u16 * dest = (u16 *)pix + (gbBorderLineSkip+2) * (register_LY + gbBorderRowSkip+1) + gbBorderColumnSkip; for(int x = 0; x < 160; ) { *dest++ = systemColorMap16[gbLineMix[x++]]; *dest++ = systemColorMap16[gbLineMix[x++]]; *dest++ = systemColorMap16[gbLineMix[x++]]; *dest++ = systemColorMap16[gbLineMix[x++]]; *dest++ = systemColorMap16[gbLineMix[x++]]; *dest++ = systemColorMap16[gbLineMix[x++]]; *dest++ = systemColorMap16[gbLineMix[x++]]; *dest++ = systemColorMap16[gbLineMix[x++]]; *dest++ = systemColorMap16[gbLineMix[x++]]; *dest++ = systemColorMap16[gbLineMix[x++]]; *dest++ = systemColorMap16[gbLineMix[x++]]; *dest++ = systemColorMap16[gbLineMix[x++]]; *dest++ = systemColorMap16[gbLineMix[x++]]; *dest++ = systemColorMap16[gbLineMix[x++]]; *dest++ = systemColorMap16[gbLineMix[x++]]; *dest++ = systemColorMap16[gbLineMix[x++]]; } if(gbBorderOn) dest += gbBorderColumnSkip; *dest++ = 0; // for filters that read one pixel more } break; case 24: { u8 *dest = (u8 *)pix + 3*(gbBorderLineSkip * (register_LY + gbBorderRowSkip) + gbBorderColumnSkip); for(int x = 0; x < 160;) { *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; dest+= 3; *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; dest+= 3; *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; dest+= 3; *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; dest+= 3; *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; dest+= 3; *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; dest+= 3; *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; dest+= 3; *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; dest+= 3; *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; dest+= 3; *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; dest+= 3; *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; dest+= 3; *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; dest+= 3; *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; dest+= 3; *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; dest+= 3; *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; dest+= 3; *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; dest+= 3; } } break; case 32: { u32 * dest = (u32 *)pix + (gbBorderLineSkip+1) * (register_LY + gbBorderRowSkip+1) + gbBorderColumnSkip; for(int x = 0; x < 160;) { *dest++ = systemColorMap32[gbLineMix[x++]]; *dest++ = systemColorMap32[gbLineMix[x++]]; *dest++ = systemColorMap32[gbLineMix[x++]]; *dest++ = systemColorMap32[gbLineMix[x++]]; *dest++ = systemColorMap32[gbLineMix[x++]]; *dest++ = systemColorMap32[gbLineMix[x++]]; *dest++ = systemColorMap32[gbLineMix[x++]]; *dest++ = systemColorMap32[gbLineMix[x++]]; *dest++ = systemColorMap32[gbLineMix[x++]]; *dest++ = systemColorMap32[gbLineMix[x++]]; *dest++ = systemColorMap32[gbLineMix[x++]]; *dest++ = systemColorMap32[gbLineMix[x++]]; *dest++ = systemColorMap32[gbLineMix[x++]]; *dest++ = systemColorMap32[gbLineMix[x++]]; *dest++ = systemColorMap32[gbLineMix[x++]]; *dest++ = systemColorMap32[gbLineMix[x++]]; } } break; } } void gbEmulate(int ticksToStop) { gbRegister tempRegister; u8 tempValue; s8 offset; clockTicks = 0; gbDmaTicks = 0; register int opcode = 0; int opcode1 = 0; int opcode2 = 0; bool execute = false; while(1) { #ifndef FINAL_VERSION if(systemDebug) { if(!(IFF & 0x80)) { if(systemDebug > 1) { sprintf(gbBuffer,"PC=%04x AF=%04x BC=%04x DE=%04x HL=%04x SP=%04x I=%04x\n", PC.W, AF.W, BC.W, DE.W,HL.W,SP.W,IFF); } else { sprintf(gbBuffer,"PC=%04x I=%02x\n", PC.W, IFF); } log(gbBuffer); } } #endif u16 oldPCW = PC.W; if(IFF & 0x80) { if(register_LCDC & 0x80) { clockTicks = gbLcdTicks; } else clockTicks = 1000; clockTicks = gbGetNextEvent(clockTicks); /*if(gbLcdTicksDelayed < clockTicks) clockTicks = gbLcdTicksDelayed; if(gbLcdLYIncrementTicksDelayed < clockTicks) clockTicks = gbLcdLYIncrementTicksDelayed; if(gbLcdLYIncrementTicks < clockTicks) clockTicks = gbLcdLYIncrementTicks; if(gbSerialOn && (gbSerialTicks < clockTicks)) clockTicks = gbSerialTicks; if(gbTimerOn && (((gbInternalTimer) & gbTimerMask[gbTimerMode])+1 < clockTicks)) clockTicks = ((gbInternalTimer) & gbTimerMask[gbTimerMode])+1; if(soundTicks && (soundTicks < clockTicks)) clockTicks = soundTicks; if ((clockTicks<=0) || (gbInterruptWait)) clockTicks = 1;*/ } else { // First we apply the clockTicks, then we execute the opcodes. opcode1 = 0; opcode2 = 0; execute = true; opcode2 = opcode1 = opcode = gbReadOpcode(PC.W++); // If HALT state was launched while IME = 0 and (register_IF & register_IE & 0x1F), // PC.W is not incremented for the first byte of the next instruction. if (IFF & 2) { PC.W--; IFF &= ~2; } clockTicks = gbCycles[opcode]; switch(opcode) { case 0xCB: // extended opcode opcode2 = opcode = gbReadOpcode(PC.W++); clockTicks = gbCyclesCB[opcode]; break; } gbOldClockTicks = clockTicks-1; gbIntBreak = 1; } if(!emulating) return; // For 'breakpoint' support (opcode 0xFC is considered as a breakpoint) if ((clockTicks==0) && execute) { PC.W = oldPCW; return; } if (!(IFF & 0x80)) clockTicks = 1; gbRedoLoop: if (gbInterruptWait) gbInterruptWait = 0; else gbInterruptLaunched = 0; // Used for the EI/DI instruction's delay. if (IFF & 0x38) { int tempIFF = (IFF >> 4) & 3; if (tempIFF <=clockTicks) { tempIFF = 0; IFF |=1; } else tempIFF -= clockTicks; IFF = (IFF & 0xCF) | (tempIFF <<4); if (IFF & 0x08) IFF &= 0x82; } if (register_LCDCBusy) { //register_LCDCBusy-=clockTicks; //if (register_LCDCBusy<0) // register_LCDCBusy = 0; } if(gbSgbMode) { if(gbSgbPacketTimeout) { gbSgbPacketTimeout -= clockTicks; if(gbSgbPacketTimeout <= 0) gbSgbResetPacketState(); } } ticksToStop -= clockTicks; // DIV register emulation gbDivTicks -= clockTicks; while(gbDivTicks <= 0) { gbMemory[0xff04] = ++register_DIV; gbDivTicks += GBDIV_CLOCK_TICKS; } if(register_LCDC & 0x80) { // LCD stuff gbLcdTicks -= clockTicks; gbLcdTicksDelayed -= clockTicks; gbLcdLYIncrementTicks -= clockTicks; gbLcdLYIncrementTicksDelayed -= clockTicks; // our counters are off, see what we need to do // This looks (and kinda is) complicated, however this // is the only way I found to emulate properly the way // the real hardware operates... while(((gbLcdTicks <= 0) && (gbLCDChangeHappened == false)) || ((gbLcdTicksDelayed <= 0) && (gbLCDChangeHappened == true)) || ((gbLcdLYIncrementTicks <= 0) && (gbLYChangeHappened == false)) || ((gbLcdLYIncrementTicksDelayed<=0) && (gbLYChangeHappened == true))) { if ((gbLcdLYIncrementTicks <= 0) && (!gbLYChangeHappened)) { gbLYChangeHappened = true; gbMemory[0xff44] = register_LY = (register_LY + 1) % 154; if (register_LY == 0x91) { /* if (IFF & 0x80) gbScreenOn = !gbScreenOn; else*/ if (register_LCDC & 0x80) gbScreenOn = true; } gbLcdLYIncrementTicks += GBLY_INCREMENT_CLOCK_TICKS; if (gbLcdMode == 1) { if(register_LY == 153) gbLcdLYIncrementTicks -= GBLY_INCREMENT_CLOCK_TICKS - gbLine99Ticks; else if(register_LY == 0) gbLcdLYIncrementTicks += GBLY_INCREMENT_CLOCK_TICKS - gbLine99Ticks; } // GB only 'bug' : Halt state is broken one tick before LY==LYC interrupt // is reflected in the registers. if ((gbHardware & 5) && (IFF & 0x80) && (register_LY == register_LYC) && (register_STAT & 0x40) && (register_LY != 0)) { if (!((gbLcdModeDelayed != 1) && (register_LY==0))) { gbInt48Signal &= ~9; gbCompareLYToLYC(); gbLYChangeHappened = false; gbMemory[0xff41] = register_STAT; gbMemory[0xff0f] = register_IF; } gbLcdLYIncrementTicksDelayed += GBLY_INCREMENT_CLOCK_TICKS - gbLine99Ticks+1; } } if ((gbLcdTicks <= 0) && (!gbLCDChangeHappened)) { gbLCDChangeHappened = true; switch(gbLcdMode) { case 0: { // H-Blank // check if we reached the V-Blank period if(register_LY == 144) { // Yes, V-Blank // set the LY increment counter if (gbHardware & 0x5) { register_IF |= 1; // V-Blank interrupt } gbInt48Signal &= ~6; if(register_STAT & 0x10) { // send LCD interrupt only if no interrupt 48h signal... if ((!(gbInt48Signal & 1)) && ((!(gbInt48Signal & 8)) || (gbHardware & 0x0a))) { register_IF |=2; gbInterruptLaunched |= 2; if (gbHardware & 0xa) gbInterruptWait = 1; } gbInt48Signal |= 2; } gbInt48Signal &= ~1; gbLcdTicks += GBLCD_MODE_1_CLOCK_TICKS; gbLcdMode = 1; } else { // go the the OAM being accessed mode gbLcdTicks += GBLCD_MODE_2_CLOCK_TICKS; gbLcdMode = 2; gbInt48Signal &= ~6; if(register_STAT & 0x20) { // send LCD interrupt only if no interrupt 48h signal... if (!gbInt48Signal) { register_IF |= 2; gbInterruptLaunched |= 2; } gbInt48Signal |= 4; } gbInt48Signal &= ~1; } } break; case 1: { // V-Blank // next mode is OAM being accessed mode gbInt48Signal &= ~5; if(register_STAT & 0x20) { // send LCD interrupt only if no interrupt 48h signal... if (!gbInt48Signal) { register_IF |= 2; gbInterruptLaunched |= 2; if ((gbHardware & 0xa) && (IFF & 0x80)) gbInterruptWait = 1; } gbInt48Signal |= 4; } gbInt48Signal &= ~2; gbLcdTicks += GBLCD_MODE_2_CLOCK_TICKS; gbLcdMode = 2; register_LY = 0x00; } break; case 2: { // OAM being accessed mode // next mode is OAM and VRAM in use if ((gbScreenOn) && (register_LCDC & 0x80)) { gbDrawSprites(false); // Used to add a one tick delay when a window line is drawn. //(fixes a part of Carmaggedon problem) if((register_LCDC & 0x01 || gbCgbMode) && (register_LCDC & 0x20) && (gbWindowLine != -2)) { int inUseRegister_WY = 0; int tempgbWindowLine = gbWindowLine; if ((tempgbWindowLine == -1) || (tempgbWindowLine>144)) { inUseRegister_WY = oldRegister_WY; if (register_LY>oldRegister_WY) tempgbWindowLine = 146; } if(register_LY >= inUseRegister_WY) { if (tempgbWindowLine == -1) tempgbWindowLine = 0; int wx = register_WX; wx -= 7; if (wx<0) wx = 0; if((wx <= 159) && (tempgbWindowLine <= 143)) for (int i = wx; i<300; i++) if (gbSpeed) gbSpritesTicks[i]+=3; else gbSpritesTicks[i]+=1; } } } gbInt48Signal &= ~7; gbLcdTicks += GBLCD_MODE_3_CLOCK_TICKS+gbSpritesTicks[299]; gbLcdMode = 3; } break; case 3: { // OAM and VRAM in use // next mode is H-Blank gbInt48Signal &= ~7; if(register_STAT & 0x08) { // send LCD interrupt only if no interrupt 48h signal... if (!(gbInt48Signal & 8)) { register_IF |= 2; if ((gbHardware & 0xa) && (IFF & 0x80)) gbInterruptWait = 1; } gbInt48Signal |= 1; } gbLcdTicks += GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]; gbLcdMode = 0; // No HDMA during HALT ! if(gbHdmaOn && (!(IFF & 0x80) || (register_IE & register_IF & 0x1f))) { gbDoHdma(); } } break; } } if ((gbLcdTicksDelayed <= 0) && (gbLCDChangeHappened)) { int framesToSkip = systemFrameSkip; if(speedup) framesToSkip = 9; // try 6 FPS during speedup //gbLcdTicksDelayed = gbLcdTicks+1; gbLCDChangeHappened = false; switch(gbLcdMode) { case 0: { // H-Blank memset(gbSCYLine,gbSCYLine[299],sizeof(gbSCYLine)); memset(gbSCXLine,gbSCXLine[299],sizeof(gbSCXLine)); memset(gbBgpLine,gbBgpLine[299],sizeof(gbBgpLine)); memset(gbObp0Line,gbObp0Line[299],sizeof(gbObp0Line)); memset(gbObp1Line,gbObp1Line[299],sizeof(gbObp1Line)); memset(gbSpritesTicks,gbSpritesTicks[299],sizeof(gbSpritesTicks)); if (gbWindowLine <0) oldRegister_WY = register_WY; // check if we reached the V-Blank period if(register_LY == 144) { // Yes, V-Blank // set the LY increment counter if(register_LCDC & 0x80) { if (gbHardware & 0xa) { register_IF |= 1; // V-Blank interrupt gbInterruptLaunched |=1; } } gbLcdTicksDelayed += GBLCD_MODE_1_CLOCK_TICKS; gbLcdMode = 1; gbFrameCount++; systemFrame(); if((gbFrameCount % 10) == 0) system10Frames(60); if(gbFrameCount >= 60) { u32 currentTime = systemGetClock(); if(currentTime != gbLastTime) systemShowSpeed(100000/(currentTime - gbLastTime)); else systemShowSpeed(0); gbLastTime = currentTime; gbFrameCount = 0; } if(systemReadJoypads()) { // read joystick if(gbSgbMode && gbSgbMultiplayer) { if(gbSgbFourPlayers) { gbJoymask[0] = systemReadJoypad(0); gbJoymask[1] = systemReadJoypad(1); gbJoymask[2] = systemReadJoypad(2); gbJoymask[3] = systemReadJoypad(3); } else { gbJoymask[0] = systemReadJoypad(0); gbJoymask[1] = systemReadJoypad(1); } } else { gbJoymask[0] = systemReadJoypad(-1); } } int newmask = gbJoymask[0] & 255; if(gbRomType == 0x22) { systemUpdateMotionSensor(); } if(newmask) { gbMemory[0xff0f] = register_IF |= 16; } newmask = (gbJoymask[0] >> 10); speedup = (newmask & 1) ? true : false; gbCapture = (newmask & 2) ? true : false; if(gbCapture && !gbCapturePrevious) { gbCaptureNumber++; systemScreenCapture(gbCaptureNumber); } gbCapturePrevious = gbCapture; if(gbFrameSkipCount >= framesToSkip) { if(!gbSgbMask) { if (gbBorderOn) gbSgbRenderBorder(); //if (gbScreenOn) systemDrawScreen(); } gbFrameSkipCount = 0; } else gbFrameSkipCount++; } else { // go the the OAM being accessed mode gbLcdTicksDelayed += GBLCD_MODE_2_CLOCK_TICKS; gbLcdMode = 2; gbInt48Signal &= ~3; } } break; case 1: { // V-Blank // next mode is OAM being accessed mode // gbScreenOn = true; oldRegister_WY = register_WY; gbLcdTicksDelayed += GBLCD_MODE_2_CLOCK_TICKS; gbLcdMode = 2; // reset the window line gbWindowLine = -1; } break; case 2: { // OAM being accessed mode // next mode is OAM and VRAM in use gbLcdTicksDelayed += GBLCD_MODE_3_CLOCK_TICKS+gbSpritesTicks[299]; gbLcdMode = 3; } break; case 3: { // OAM and VRAM in use // next mode is H-Blank if((register_LY < 144) && (register_LCDC & 0x80) && gbScreenOn) { if(!gbSgbMask) { if(gbFrameSkipCount >= framesToSkip) { if (!gbBlackScreen) { gbRenderLine(); gbDrawSprites(true); } else if (gbBlackScreen) { u16 color = gbColorOption ? gbColorFilter[0] : 0; if (!gbCgbMode) color = gbColorOption ? gbColorFilter[gbPalette[3] & 0x7FFF] : gbPalette[3] & 0x7FFF; for(int i = 0; i < 160; i++) { gbLineMix[i] = color; gbLineBuffer[i] = 0; } } gbDrawLine(); } } } gbLcdTicksDelayed += GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]; gbLcdMode = 0; } break; } } if ((gbLcdLYIncrementTicksDelayed <= 0) && (gbLYChangeHappened == true)) { gbLYChangeHappened = false; if (!((gbLcdMode != 1) && (register_LY==0))) { { gbInt48Signal &= ~8; gbCompareLYToLYC(); if ((gbInt48Signal == 8) && (!((register_LY == 0) && (gbHardware & 1)))) gbInterruptLaunched |= 2; if ((gbHardware & (gbSpeed ? 8 : 2)) && (register_LY==0) && ((register_STAT & 0x44) == 0x44) && (gbLcdLYIncrementTicksDelayed==0)) { gbInterruptWait = 1; } } } gbLcdLYIncrementTicksDelayed += GBLY_INCREMENT_CLOCK_TICKS; if (gbLcdMode == 1) { if(register_LY == 153) gbLcdLYIncrementTicksDelayed -= GBLY_INCREMENT_CLOCK_TICKS - gbLine99Ticks; else if(register_LY == 0) gbLcdLYIncrementTicksDelayed += GBLY_INCREMENT_CLOCK_TICKS - gbLine99Ticks; } gbMemory[0xff0f] = register_IF; gbMemory[0xff41] = register_STAT; } } gbMemory[0xff0f] = register_IF; gbMemory[0xff41] = register_STAT = (register_STAT & 0xfc) | gbLcdModeDelayed; } else { // Used to update the screen with white lines when it's off. // (it looks strange, but it's kinda accurate :p) // You can try the Mario Demo Vx.x for exemple // (check the bottom 2 lines while moving) if (!gbWhiteScreen) { gbScreenTicks -= clockTicks; gbLcdLYIncrementTicks -= clockTicks; while (gbLcdLYIncrementTicks <=0) { register_LY = ((register_LY+1)%154); gbLcdLYIncrementTicks+=GBLY_INCREMENT_CLOCK_TICKS; } if (gbScreenTicks <= 0) { gbWhiteScreen = 1; u8 register_LYLcdOff = ((register_LY+154)%154); for (register_LY=0;register_LY <= 0x90;register_LY++) { u16 color = gbColorOption ? gbColorFilter[0x7FFF] : 0x7FFF; if (!gbCgbMode) color = gbColorOption ? gbColorFilter[gbPalette[0] & 0x7FFF] : gbPalette[0] & 0x7FFF; for(int i = 0; i < 160; i++) { gbLineMix[i] = color; gbLineBuffer[i] = 0; } gbDrawLine(); } register_LY = register_LYLcdOff; } } if (gbWhiteScreen) { gbLcdLYIncrementTicks -= clockTicks; while (gbLcdLYIncrementTicks <=0) { register_LY = ((register_LY+1)%154); gbLcdLYIncrementTicks+=GBLY_INCREMENT_CLOCK_TICKS; if (register_LY<144) { u16 color = gbColorOption ? gbColorFilter[0x7FFF] : 0x7FFF; if (!gbCgbMode) color = gbColorOption ? gbColorFilter[gbPalette[0] & 0x7FFF] : gbPalette[0] & 0x7FFF; for(int i = 0; i < 160; i++) { gbLineMix[i] = color; gbLineBuffer[i] = 0; } gbDrawLine(); } else if ((register_LY==144) && (!systemFrameSkip)) { int framesToSkip = systemFrameSkip; if(speedup) framesToSkip = 9; // try 6 FPS during speedup if((gbFrameSkipCount >= framesToSkip) || (gbWhiteScreen == 1)) { gbWhiteScreen = 2; if(!gbSgbMask) { if (gbBorderOn) gbSgbRenderBorder(); //if (gbScreenOn) systemDrawScreen(); } } if(systemReadJoypads()) { // read joystick if(gbSgbMode && gbSgbMultiplayer) { if(gbSgbFourPlayers) { gbJoymask[0] = systemReadJoypad(0); gbJoymask[1] = systemReadJoypad(1); gbJoymask[2] = systemReadJoypad(2); gbJoymask[3] = systemReadJoypad(3); } else { gbJoymask[0] = systemReadJoypad(0); gbJoymask[1] = systemReadJoypad(1); } } else { gbJoymask[0] = systemReadJoypad(-1); } } gbFrameCount++; systemFrame(); if((gbFrameCount % 10) == 0) system10Frames(60); if(gbFrameCount >= 60) { u32 currentTime = systemGetClock(); if(currentTime != gbLastTime) systemShowSpeed(100000/(currentTime - gbLastTime)); else systemShowSpeed(0); gbLastTime = currentTime; gbFrameCount = 0; } } } } } gbMemory[0xff41] = register_STAT; // serial emulation if(gbSerialOn) { #ifdef LINK_EMULATION if(linkConnected) { gbSerialTicks -= clockTicks; while(gbSerialTicks <= 0) { // increment number of shifted bits gbSerialBits++; linkProc(); if(gbSerialOn && (gbMemory[0xff02] & 1)) { if(gbSerialBits == 8) { gbSerialBits = 0; gbMemory[0xff01] = 0xff; gbMemory[0xff02] &= 0x7f; gbSerialOn = 0; gbMemory[0xff0f] = register_IF |= 8; gbSerialTicks = 0; } } gbSerialTicks += GBSERIAL_CLOCK_TICKS; } } else { #endif if(gbMemory[0xff02] & 1) { gbSerialTicks -= clockTicks; // overflow while(gbSerialTicks <= 0) { // shift serial byte to right and put a 1 bit in its place // gbMemory[0xff01] = 0x80 | (gbMemory[0xff01]>>1); // increment number of shifted bits gbSerialBits++; if(gbSerialBits == 8) { // end of transmission if(gbSerialFunction) // external device gbMemory[0xff01] = gbSerialFunction(gbMemory[0xff01]); else gbMemory[0xff01] = 0xff; gbSerialTicks = 0; gbMemory[0xff02] &= 0x7f; gbSerialOn = 0; gbMemory[0xff0f] = register_IF |= 8; gbSerialBits = 0; } else gbSerialTicks += GBSERIAL_CLOCK_TICKS; } } #ifdef LINK_EMULATION } #endif } soundTicks -= clockTicks; if ( !gbSpeed ) soundTicks -= clockTicks; while(soundTicks < 0) { soundTicks += SOUND_CLOCK_TICKS; gbSoundTick(); } // timer emulation if(gbTimerOn) { gbTimerTicks= ((gbInternalTimer) & gbTimerMask[gbTimerMode])+1-clockTicks; while(gbTimerTicks <= 0) { register_TIMA++; // timer overflow! if((register_TIMA & 0xff) == 0) { // reload timer modulo register_TIMA = register_TMA; // flag interrupt gbMemory[0xff0f] = register_IF |= 4; } gbTimerTicks += gbTimerClockTicks; } gbTimerOnChange = false; gbTimerModeChange = false; gbMemory[0xff05] = register_TIMA; } gbInternalTimer -= clockTicks; while (gbInternalTimer<0) gbInternalTimer+=0x100; clockTicks = 0; if (gbIntBreak == 1) { gbIntBreak = 0; if ((register_IE & register_IF & gbInterruptLaunched & 0x3) && ((IFF & 0x81) == 1) && (!gbInterruptWait) && (execute)) { gbIntBreak = 2; PC.W = oldPCW; execute = false; gbOldClockTicks = 0; } if (gbOldClockTicks) { clockTicks = gbOldClockTicks; gbOldClockTicks = 0; goto gbRedoLoop; } } // Executes the opcode(s), and apply the instruction's remaining clockTicks (if any). if (execute) { switch(opcode1) { case 0xCB: // extended opcode switch(opcode2) { #include "gbCodesCB.h" } break; #include "gbCodes.h" } execute = false; if (clockTicks) { gbDmaTicks += clockTicks; clockTicks = 0; } } if (gbDmaTicks) { clockTicks = gbGetNextEvent(gbDmaTicks); if (clockTicks<=gbDmaTicks) gbDmaTicks -= clockTicks; else { clockTicks = gbDmaTicks; gbDmaTicks = 0; } goto gbRedoLoop; } // Remove the 'if an IE is pending' flag if IE has finished being executed. if ((IFF & 0x40) && !(IFF & 0x30)) IFF &= 0x81; if ((register_IE & register_IF & 0x1f) && (IFF & 0x81) && (!gbInterruptWait)) { if (IFF & 1) { // Add 5 ticks for the interrupt execution time gbDmaTicks += 5; if (gbIntBreak == 2) { gbDmaTicks--; gbIntBreak = 0; } if(register_IF & register_IE & 1) gbVblank_interrupt(); else if(register_IF & register_IE & 2) gbLcd_interrupt(); else if(register_IF & register_IE & 4) gbTimer_interrupt(); else if(register_IF & register_IE & 8) gbSerial_interrupt(); else if(register_IF & register_IE & 16) gbJoypad_interrupt(); } IFF &= ~0x81; } if (IFF & 0x08) IFF &=~0x79; // Used to apply the interrupt's execution time. if (gbDmaTicks) { clockTicks = gbGetNextEvent(gbDmaTicks); if (clockTicks<=gbDmaTicks) gbDmaTicks -= clockTicks; else { clockTicks = gbDmaTicks; gbDmaTicks = 0; } goto gbRedoLoop; } gbBlackScreen = false; if((ticksToStop <= 0)) { if(!(register_LCDC & 0x80)) { if(systemReadJoypads()) { // read joystick if(gbSgbMode && gbSgbMultiplayer) { if(gbSgbFourPlayers) { gbJoymask[0] = systemReadJoypad(0); gbJoymask[1] = systemReadJoypad(1); gbJoymask[2] = systemReadJoypad(2); gbJoymask[3] = systemReadJoypad(3); } else { gbJoymask[0] = systemReadJoypad(0); gbJoymask[1] = systemReadJoypad(1); } } else { gbJoymask[0] = systemReadJoypad(-1); } } } return; } } } struct EmulatedSystem GBSystem = { // emuMain gbEmulate, // emuReset gbReset, // emuCleanUp gbCleanUp, // emuReadBattery gbReadBatteryFile, // emuWriteBattery gbWriteBatteryFile, // emuReadState gbReadSaveState, // emuWriteState gbWriteSaveState, // emuReadMemState gbReadMemSaveState, // emuWriteMemState gbWriteMemSaveState, // emuWritePNG gbWritePNGFile, // emuWriteBMP gbWriteBMPFile, // emuUpdateCPSR NULL, // emuHasDebugger false, // emuCount #ifdef FINAL_VERSION 70000/4, #else 1000, #endif }; /**************************************************************************** * Nintendo Wii/Gamecube Port Additions * * Duplicate versions of save functions above, using memory * I want to kill whoever wrote so many stupid functions, and did so without * doing it memory-based * Tantric - October 2008 ***************************************************************************/ int MemgbWriteSaveMBC1(char * membuffer) { if (gbRam) { memcpy(membuffer, gbRam, (gbRamSizeMask + 1)); return (gbRamSizeMask + 1); } return 0; } int MemgbWriteSaveMBC2(char * membuffer) { if (gbRam) { memcpy(membuffer, &gbMemory[0xa000], 256); return 256; } return 0; } int MemgbWriteSaveMBC3(char * membuffer, bool extendedSave) { int offset = 0; if (gbRam || extendedSave) { if (gbRam) { memcpy(membuffer, gbRam, (gbRamSizeMask + 1)); offset += (gbRamSizeMask + 1); } if (extendedSave) { memcpy(membuffer+offset, &gbDataMBC3.mapperSeconds, (10 * sizeof(int) + sizeof(time_t))); offset += (10 * sizeof(int) + sizeof(time_t)); } } return offset; } int MemgbWriteSaveMBC5(char * membuffer) { if (gbRam) { memcpy(membuffer, gbRam, (gbRamSizeMask + 1)); return (gbRamSizeMask + 1); } return 0; } int MemgbWriteSaveMBC7(char * membuffer) { if (gbRam) { memcpy(membuffer, &gbMemory[0xa000], 256); return 256; } return 0; } int MemgbWriteSaveTAMA5(char * membuffer, bool extendedSave) { int offset = 0; if (gbRam) { memcpy(membuffer, gbRam, (gbRamSizeMask + 1)); offset += (gbRamSizeMask + 1); } memcpy(membuffer+offset, gbTAMA5ram, (gbTAMA5ramSize)); offset += (gbTAMA5ramSize); if (extendedSave) { memcpy(membuffer+offset, &gbDataTAMA5.mapperSeconds, (14 * sizeof(int) + sizeof(time_t))); offset += (14 * sizeof(int) + sizeof(time_t)); } return offset; } int MemgbWriteSaveMMM01(char * membuffer) { if (gbRam) { memcpy(membuffer, gbRam, (gbRamSizeMask + 1)); return (gbRamSizeMask + 1); } return 0; } bool MemgbReadSaveMBC1(char * membuffer, int read) { if (gbRam) { if (read != (gbRamSizeMask + 1)) return false; else memcpy(gbRam, membuffer, read); return true; } return false; } bool MemgbReadSaveMBC2(char * membuffer, int read) { if (gbRam) { if (read != 256) return false; else memcpy(&gbMemory[0xa000], membuffer, read); return true; } return false; } bool MemgbReadSaveMBC3(char * membuffer, int read) { int offset = 0; if (gbRam) { if(read < (gbRamSizeMask + 1)) return false; memcpy(gbRam, membuffer, (gbRamSizeMask + 1)); offset += (gbRamSizeMask + 1); } int gbRomType = gbRom[0x147]; if ((gbRomType == 0xf) || (gbRomType == 0x10)) { if((uint)read < (offset + sizeof(int) * 10 + sizeof(time_t))) return false; memcpy(&gbDataMBC3.mapperSeconds, membuffer+offset, sizeof(int) * 10 + sizeof(time_t)); } return true; } bool MemgbReadSaveMBC5(char * membuffer, int read) { if (gbRam) { if (read != (gbRamSizeMask + 1)) return false; else memcpy(gbRam, membuffer, read); return true; } return false; } bool MemgbReadSaveMBC7(char * membuffer, int read) { if (gbRam) { if (read != 256) return false; else memcpy(&gbMemory[0xa000], membuffer, read); return true; } return false; } bool MemgbReadSaveTAMA5(char * membuffer, int read) { if (gbRam) { if (gbRamSizeMask + gbTAMA5ramSize + 1) return false; memcpy(gbRam, membuffer, (gbRamSizeMask + 1)); int offset = (gbRamSizeMask + 1); memcpy(&gbDataTAMA5.mapperSeconds, membuffer+offset, sizeof(int) * 14 + sizeof(time_t)); return true; } return false; } bool MemgbReadSaveMMM01(char * membuffer, int read) { if (gbRam) { if (read != (gbRamSizeMask + 1)) return false; else memcpy(gbRam, membuffer, read); return true; } return false; } int MemgbWriteBatteryFile(char * membuffer) { int result = 0; if(gbBattery) { int type = gbRom[0x147]; bool extendedSave = true; switch(type) { case 0x03: result = MemgbWriteSaveMBC1(membuffer); break; case 0x06: result = MemgbWriteSaveMBC2(membuffer); break; case 0x0d: result = MemgbWriteSaveMMM01(membuffer); break; case 0x0f: case 0x10: result = MemgbWriteSaveMBC3(membuffer, extendedSave); break; case 0x13: case 0xfc: result = MemgbWriteSaveMBC3(membuffer, false); case 0x1b: case 0x1e: result = MemgbWriteSaveMBC5(membuffer); break; case 0x22: result = MemgbWriteSaveMBC7(membuffer); break; case 0xfd: result = MemgbWriteSaveTAMA5(membuffer, extendedSave); break; case 0xff: result = MemgbWriteSaveMBC1(membuffer); break; } } return result; } bool MemgbReadBatteryFile(char * membuffer, int read) { bool res = false; if (gbBattery) { int type = gbRom[0x147]; switch (type) { case 0x03: res = MemgbReadSaveMBC1(membuffer, read); break; case 0x06: res = MemgbReadSaveMBC2(membuffer, read); break; case 0x0d: res = MemgbReadSaveMMM01(membuffer, read); break; case 0x0f: case 0x10: res = MemgbReadSaveMBC3(membuffer, read); if (!res) { time(&gbDataMBC3.mapperLastTime); struct tm *lt; lt = localtime(&gbDataMBC3.mapperLastTime); gbDataMBC3.mapperSeconds = lt->tm_sec; gbDataMBC3.mapperMinutes = lt->tm_min; gbDataMBC3.mapperHours = lt->tm_hour; gbDataMBC3.mapperDays = lt->tm_yday & 255; gbDataMBC3.mapperControl = (gbDataMBC3.mapperControl & 0xfe) | (lt->tm_yday > 255 ? 1 : 0); res = 0; break; } break; case 0x13: case 0xfc: res = MemgbReadSaveMBC3(membuffer, read); break; case 0x1b: case 0x1e: res = MemgbReadSaveMBC5(membuffer, read); break; case 0x22: res = MemgbReadSaveMBC7(membuffer, read); break; case 0xfd: res = MemgbReadSaveTAMA5(membuffer, read); if (!res) { u8 gbDaysinMonth[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; time(&gbDataTAMA5.mapperLastTime); struct tm *lt; lt = localtime(&gbDataTAMA5.mapperLastTime); gbDataTAMA5.mapperSeconds = lt->tm_sec; gbDataTAMA5.mapperMinutes = lt->tm_min; gbDataTAMA5.mapperHours = lt->tm_hour; gbDataTAMA5.mapperDays = 1; gbDataTAMA5.mapperMonths = 1; gbDataTAMA5.mapperYears = 1970; int days = lt->tm_yday + 365 * 3; while (days) { gbDataTAMA5.mapperDays++; days--; if (gbDataTAMA5.mapperDays > gbDaysinMonth[gbDataTAMA5.mapperMonths - 1]) { gbDataTAMA5.mapperDays = 1; gbDataTAMA5.mapperMonths++; if (gbDataTAMA5.mapperMonths > 12) { gbDataTAMA5.mapperMonths = 1; gbDataTAMA5.mapperYears++; if ((gbDataTAMA5.mapperYears & 3) == 0) gbDaysinMonth[1] = 29; else gbDaysinMonth[1] = 28; } } } gbDataTAMA5.mapperControl = (gbDataTAMA5.mapperControl & 0xfe) | (lt->tm_yday > 255 ? 1 : 0); res = false; break; } break; case 0xff: res = MemgbReadSaveMBC1(membuffer, read); break; } } systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; return res; }