rlm@1: #include rlm@1: #include rlm@1: #include rlm@1: #include rlm@1: rlm@1: #include "../Port.h" rlm@1: #include "../NLS.h" rlm@1: #include "GB.h" rlm@1: #include "gbCheats.h" rlm@1: #include "gbGlobals.h" rlm@1: #include "gbMemory.h" rlm@1: #include "gbSGB.h" rlm@1: #include "gbSound.h" rlm@1: #include "../common/unzip.h" rlm@1: #include "../common/Util.h" rlm@1: #include "../common/System.h" rlm@1: #include "../common/movie.h" rlm@1: #include "../common/vbalua.h" rlm@1: rlm@1: #ifdef __GNUC__ rlm@1: #define _stricmp strcasecmp rlm@1: #endif rlm@1: rlm@1: // FIXME: constant (GB) or boolean (GBA)?! rlm@1: #define C_FLAG 0x10 rlm@1: #define H_FLAG 0x20 rlm@1: #define N_FLAG 0x40 rlm@1: #define Z_FLAG 0x80 rlm@1: extern soundtick_t GB_USE_TICKS_AS; rlm@1: rlm@1: u8 * origPix = NULL; rlm@1: extern u8 * pix; rlm@1: extern u32 extButtons; rlm@1: extern bool8 capturePrevious; rlm@1: extern int32 captureNumber; rlm@1: extern bool8 speedup; rlm@1: rlm@1: bool gbUpdateSizes(); rlm@1: rlm@1: // debugging rlm@1: bool memorydebug = false; rlm@1: char gbBuffer[2048]; rlm@1: rlm@1: extern u16 gbLineMix[160]; rlm@1: rlm@1: // mappers rlm@1: void (*mapper)(u16, u8) = NULL; rlm@1: void (*mapperRAM)(u16, u8) = NULL; rlm@1: u8 (*mapperReadRAM)(u16) = NULL; rlm@1: rlm@1: // registers rlm@1: gbRegister PC; rlm@1: gbRegister SP; rlm@1: gbRegister AF; rlm@1: gbRegister BC; rlm@1: gbRegister DE; rlm@1: gbRegister HL; rlm@1: u16 IFF; rlm@1: // 0xff04 rlm@1: u8 register_DIV = 0; rlm@1: // 0xff05 rlm@1: u8 register_TIMA = 0; rlm@1: // 0xff06 rlm@1: u8 register_TMA = 0; rlm@1: // 0xff07 rlm@1: u8 register_TAC = 0; rlm@1: // 0xff0f rlm@1: u8 register_IF = 0; rlm@1: // 0xff40 rlm@1: u8 register_LCDC = 0; rlm@1: // 0xff41 rlm@1: u8 register_STAT = 0; rlm@1: // 0xff42 rlm@1: u8 register_SCY = 0; rlm@1: // 0xff43 rlm@1: u8 register_SCX = 0; rlm@1: // 0xff44 rlm@1: u8 register_LY = 0; rlm@1: // 0xff45 rlm@1: u8 register_LYC = 0; rlm@1: // 0xff46 rlm@1: u8 register_DMA = 0; rlm@1: // 0xff4a rlm@1: u8 register_WY = 0; rlm@1: // 0xff4b rlm@1: u8 register_WX = 0; rlm@1: // 0xff4f rlm@1: u8 register_VBK = 0; rlm@1: // 0xff51 rlm@1: u8 register_HDMA1 = 0; rlm@1: // 0xff52 rlm@1: u8 register_HDMA2 = 0; rlm@1: // 0xff53 rlm@1: u8 register_HDMA3 = 0; rlm@1: // 0xff54 rlm@1: u8 register_HDMA4 = 0; rlm@1: // 0xff55 rlm@1: u8 register_HDMA5 = 0; rlm@1: // 0xff70 rlm@1: u8 register_SVBK = 0; rlm@1: // 0xffff rlm@1: u8 register_IE = 0; rlm@1: rlm@1: // ticks definition rlm@1: int32 GBDIV_CLOCK_TICKS = 64; rlm@1: int32 GBLCD_MODE_0_CLOCK_TICKS = 51; rlm@1: int32 GBLCD_MODE_1_CLOCK_TICKS = 1140; rlm@1: int32 GBLCD_MODE_2_CLOCK_TICKS = 20; rlm@1: int32 GBLCD_MODE_3_CLOCK_TICKS = 43; rlm@1: int32 GBLY_INCREMENT_CLOCK_TICKS = 114; rlm@1: int32 GBTIMER_MODE_0_CLOCK_TICKS = 256; rlm@1: int32 GBTIMER_MODE_1_CLOCK_TICKS = 4; rlm@1: int32 GBTIMER_MODE_2_CLOCK_TICKS = 16; rlm@1: int32 GBTIMER_MODE_3_CLOCK_TICKS = 64; rlm@1: int32 GBSERIAL_CLOCK_TICKS = 128; rlm@1: int32 GBSYNCHRONIZE_CLOCK_TICKS = 52920; rlm@1: rlm@1: // state variables rlm@1: rlm@1: // interrupt rlm@1: int32 gbInterrupt = 0; rlm@1: int32 gbInterruptWait = 0; rlm@1: // serial rlm@1: int32 gbSerialOn = 0; rlm@1: int32 gbSerialTicks = 0; rlm@1: int32 gbSerialBits = 0; rlm@1: // timer rlm@1: int32 gbTimerOn = 0; rlm@1: int32 gbTimerTicks = 0; rlm@1: int32 gbTimerClockTicks = 0; rlm@1: int32 gbTimerMode = 0; rlm@1: // lcd rlm@1: int32 gbLcdMode = 2; rlm@1: int32 gbLcdTicks = GBLCD_MODE_2_CLOCK_TICKS; rlm@1: int32 gbLcdLYIncrementTicks = 0; rlm@1: // div rlm@1: int32 gbDivTicks = GBDIV_CLOCK_TICKS; rlm@1: // cgb rlm@1: int32 gbVramBank = 0; rlm@1: int32 gbWramBank = 1; rlm@1: int32 gbHdmaSource = 0x0000; rlm@1: int32 gbHdmaDestination = 0x8000; rlm@1: int32 gbHdmaBytes = 0x0000; rlm@1: int32 gbHdmaOn = 0; rlm@1: int32 gbSpeed = 0; rlm@1: // frame counting rlm@1: int32 gbFrameCount = 0; rlm@1: int32 gbFrameSkip = 0; rlm@1: int32 gbFrameSkipCount = 0; rlm@1: // timing rlm@1: u32 gbLastTime = 0; rlm@1: u32 gbElapsedTime = 0; rlm@1: u32 gbTimeNow = 0; rlm@1: int32 gbSynchronizeTicks = GBSYNCHRONIZE_CLOCK_TICKS; rlm@1: int32 gbDMASpeedVersion = 1; rlm@1: // emulator features rlm@1: int32 gbBattery = 0; rlm@1: int32 gbJoymask[4] = { 0, 0, 0, 0 }; rlm@1: rlm@1: int32 gbEchoRAMFixOn = 1; rlm@1: rlm@1: static bool newFrame = true; rlm@1: static bool pauseAfterFrameAdvance = false; rlm@1: rlm@1: int32 gbRomSizes[] = { 0x00008000, // 32K rlm@33: 0x00010000, // 64K rlm@33: 0x00020000, // 128K rlm@33: 0x00040000, // 256K rlm@33: 0x00080000, // 512K rlm@33: 0x00100000, // 1024K rlm@33: 0x00200000, // 2048K rlm@33: 0x00400000, // 4096K rlm@33: 0x00800000 // 8192K rlm@1: }; rlm@1: int32 gbRomSizesMasks[] = { 0x00007fff, rlm@33: 0x0000ffff, rlm@33: 0x0001ffff, rlm@33: 0x0003ffff, rlm@33: 0x0007ffff, rlm@33: 0x000fffff, rlm@33: 0x001fffff, rlm@33: 0x003fffff, rlm@33: 0x007fffff }; rlm@1: rlm@1: int32 gbRamSizes[6] = { 0x00000000, // 0K rlm@33: 0x00000800, // 2K rlm@33: 0x00002000, // 8K rlm@33: 0x00008000, // 32K rlm@33: 0x00020000, // 128K rlm@33: 0x00010000 // 64K rlm@1: }; rlm@1: rlm@1: int32 gbRamSizesMasks[6] = { 0x00000000, rlm@33: 0x000007ff, rlm@33: 0x00001fff, rlm@33: 0x00007fff, rlm@33: 0x0001ffff, rlm@33: 0x0000ffff }; rlm@1: rlm@1: int32 gbCycles[] = rlm@33: { rlm@33: // 0 1 2 3 4 5 6 7 8 9 a b c d e f rlm@33: 1, 3, 2, 2, 1, 1, 2, 1, 5, 2, 2, 2, 1, 1, 2, 1, // 0 rlm@33: 1, 3, 2, 2, 1, 1, 2, 1, 3, 2, 2, 2, 1, 1, 2, 1, // 1 rlm@33: 2, 3, 2, 2, 1, 1, 2, 1, 2, 2, 2, 2, 1, 1, 2, 1, // 2 rlm@33: 2, 3, 2, 2, 3, 3, 3, 1, 2, 2, 2, 2, 1, 1, 2, 1, // 3 rlm@33: 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 4 rlm@33: 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 5 rlm@33: 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 6 rlm@33: 2, 2, 2, 2, 2, 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, // 7 rlm@33: 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 8 rlm@33: 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 9 rlm@33: 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // a rlm@33: 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // b rlm@33: 2, 3, 3, 4, 3, 4, 2, 4, 2, 4, 3, 2, 3, 6, 2, 4, // c rlm@33: 2, 3, 3, 0, 3, 4, 2, 4, 2, 4, 3, 0, 3, 0, 2, 4, // d rlm@33: 3, 3, 2, 0, 0, 4, 2, 4, 4, 1, 4, 0, 0, 0, 2, 4, // e rlm@33: 3, 3, 2, 1, 0, 4, 2, 4, 3, 2, 4, 1, 0, 0, 2, 4 // f rlm@33: }; rlm@1: rlm@1: int32 gbCyclesCB[] = rlm@33: { rlm@33: // 0 1 2 3 4 5 6 7 8 9 a b c d e f rlm@33: 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 0 rlm@33: 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 1 rlm@33: 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 2 rlm@33: 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 3 rlm@33: 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // 4 rlm@33: 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // 5 rlm@33: 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // 6 rlm@33: 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // 7 rlm@33: 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // 8 rlm@33: 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // 9 rlm@33: 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // a rlm@33: 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // b rlm@33: 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // c rlm@33: 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // d rlm@33: 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // e rlm@33: 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2 // f rlm@33: }; rlm@1: rlm@1: u16 DAATable[] = rlm@33: { rlm@33: 0x0080, 0x0100, 0x0200, 0x0300, 0x0400, 0x0500, 0x0600, 0x0700, rlm@33: 0x0800, 0x0900, 0x1020, 0x1120, 0x1220, 0x1320, 0x1420, 0x1520, rlm@33: 0x1000, 0x1100, 0x1200, 0x1300, 0x1400, 0x1500, 0x1600, 0x1700, rlm@33: 0x1800, 0x1900, 0x2020, 0x2120, 0x2220, 0x2320, 0x2420, 0x2520, rlm@33: 0x2000, 0x2100, 0x2200, 0x2300, 0x2400, 0x2500, 0x2600, 0x2700, rlm@33: 0x2800, 0x2900, 0x3020, 0x3120, 0x3220, 0x3320, 0x3420, 0x3520, rlm@33: 0x3000, 0x3100, 0x3200, 0x3300, 0x3400, 0x3500, 0x3600, 0x3700, rlm@33: 0x3800, 0x3900, 0x4020, 0x4120, 0x4220, 0x4320, 0x4420, 0x4520, rlm@33: 0x4000, 0x4100, 0x4200, 0x4300, 0x4400, 0x4500, 0x4600, 0x4700, rlm@33: 0x4800, 0x4900, 0x5020, 0x5120, 0x5220, 0x5320, 0x5420, 0x5520, rlm@33: 0x5000, 0x5100, 0x5200, 0x5300, 0x5400, 0x5500, 0x5600, 0x5700, rlm@33: 0x5800, 0x5900, 0x6020, 0x6120, 0x6220, 0x6320, 0x6420, 0x6520, rlm@33: 0x6000, 0x6100, 0x6200, 0x6300, 0x6400, 0x6500, 0x6600, 0x6700, rlm@33: 0x6800, 0x6900, 0x7020, 0x7120, 0x7220, 0x7320, 0x7420, 0x7520, rlm@33: 0x7000, 0x7100, 0x7200, 0x7300, 0x7400, 0x7500, 0x7600, 0x7700, rlm@33: 0x7800, 0x7900, 0x8020, 0x8120, 0x8220, 0x8320, 0x8420, 0x8520, rlm@33: 0x8000, 0x8100, 0x8200, 0x8300, 0x8400, 0x8500, 0x8600, 0x8700, rlm@33: 0x8800, 0x8900, 0x9020, 0x9120, 0x9220, 0x9320, 0x9420, 0x9520, rlm@33: 0x9000, 0x9100, 0x9200, 0x9300, 0x9400, 0x9500, 0x9600, 0x9700, rlm@33: 0x9800, 0x9900, 0x00B0, 0x0130, 0x0230, 0x0330, 0x0430, 0x0530, rlm@33: 0x0090, 0x0110, 0x0210, 0x0310, 0x0410, 0x0510, 0x0610, 0x0710, rlm@33: 0x0810, 0x0910, 0x1030, 0x1130, 0x1230, 0x1330, 0x1430, 0x1530, rlm@33: 0x1010, 0x1110, 0x1210, 0x1310, 0x1410, 0x1510, 0x1610, 0x1710, rlm@33: 0x1810, 0x1910, 0x2030, 0x2130, 0x2230, 0x2330, 0x2430, 0x2530, rlm@33: 0x2010, 0x2110, 0x2210, 0x2310, 0x2410, 0x2510, 0x2610, 0x2710, rlm@33: 0x2810, 0x2910, 0x3030, 0x3130, 0x3230, 0x3330, 0x3430, 0x3530, rlm@33: 0x3010, 0x3110, 0x3210, 0x3310, 0x3410, 0x3510, 0x3610, 0x3710, rlm@33: 0x3810, 0x3910, 0x4030, 0x4130, 0x4230, 0x4330, 0x4430, 0x4530, rlm@33: 0x4010, 0x4110, 0x4210, 0x4310, 0x4410, 0x4510, 0x4610, 0x4710, rlm@33: 0x4810, 0x4910, 0x5030, 0x5130, 0x5230, 0x5330, 0x5430, 0x5530, rlm@33: 0x5010, 0x5110, 0x5210, 0x5310, 0x5410, 0x5510, 0x5610, 0x5710, rlm@33: 0x5810, 0x5910, 0x6030, 0x6130, 0x6230, 0x6330, 0x6430, 0x6530, rlm@33: 0x6010, 0x6110, 0x6210, 0x6310, 0x6410, 0x6510, 0x6610, 0x6710, rlm@33: 0x6810, 0x6910, 0x7030, 0x7130, 0x7230, 0x7330, 0x7430, 0x7530, rlm@33: 0x7010, 0x7110, 0x7210, 0x7310, 0x7410, 0x7510, 0x7610, 0x7710, rlm@33: 0x7810, 0x7910, 0x8030, 0x8130, 0x8230, 0x8330, 0x8430, 0x8530, rlm@33: 0x8010, 0x8110, 0x8210, 0x8310, 0x8410, 0x8510, 0x8610, 0x8710, rlm@33: 0x8810, 0x8910, 0x9030, 0x9130, 0x9230, 0x9330, 0x9430, 0x9530, rlm@33: 0x9010, 0x9110, 0x9210, 0x9310, 0x9410, 0x9510, 0x9610, 0x9710, rlm@33: 0x9810, 0x9910, 0xA030, 0xA130, 0xA230, 0xA330, 0xA430, 0xA530, rlm@33: 0xA010, 0xA110, 0xA210, 0xA310, 0xA410, 0xA510, 0xA610, 0xA710, rlm@33: 0xA810, 0xA910, 0xB030, 0xB130, 0xB230, 0xB330, 0xB430, 0xB530, rlm@33: 0xB010, 0xB110, 0xB210, 0xB310, 0xB410, 0xB510, 0xB610, 0xB710, rlm@33: 0xB810, 0xB910, 0xC030, 0xC130, 0xC230, 0xC330, 0xC430, 0xC530, rlm@33: 0xC010, 0xC110, 0xC210, 0xC310, 0xC410, 0xC510, 0xC610, 0xC710, rlm@33: 0xC810, 0xC910, 0xD030, 0xD130, 0xD230, 0xD330, 0xD430, 0xD530, rlm@33: 0xD010, 0xD110, 0xD210, 0xD310, 0xD410, 0xD510, 0xD610, 0xD710, rlm@33: 0xD810, 0xD910, 0xE030, 0xE130, 0xE230, 0xE330, 0xE430, 0xE530, rlm@33: 0xE010, 0xE110, 0xE210, 0xE310, 0xE410, 0xE510, 0xE610, 0xE710, rlm@33: 0xE810, 0xE910, 0xF030, 0xF130, 0xF230, 0xF330, 0xF430, 0xF530, rlm@33: 0xF010, 0xF110, 0xF210, 0xF310, 0xF410, 0xF510, 0xF610, 0xF710, rlm@33: 0xF810, 0xF910, 0x00B0, 0x0130, 0x0230, 0x0330, 0x0430, 0x0530, rlm@33: 0x0090, 0x0110, 0x0210, 0x0310, 0x0410, 0x0510, 0x0610, 0x0710, rlm@33: 0x0810, 0x0910, 0x1030, 0x1130, 0x1230, 0x1330, 0x1430, 0x1530, rlm@33: 0x1010, 0x1110, 0x1210, 0x1310, 0x1410, 0x1510, 0x1610, 0x1710, rlm@33: 0x1810, 0x1910, 0x2030, 0x2130, 0x2230, 0x2330, 0x2430, 0x2530, rlm@33: 0x2010, 0x2110, 0x2210, 0x2310, 0x2410, 0x2510, 0x2610, 0x2710, rlm@33: 0x2810, 0x2910, 0x3030, 0x3130, 0x3230, 0x3330, 0x3430, 0x3530, rlm@33: 0x3010, 0x3110, 0x3210, 0x3310, 0x3410, 0x3510, 0x3610, 0x3710, rlm@33: 0x3810, 0x3910, 0x4030, 0x4130, 0x4230, 0x4330, 0x4430, 0x4530, rlm@33: 0x4010, 0x4110, 0x4210, 0x4310, 0x4410, 0x4510, 0x4610, 0x4710, rlm@33: 0x4810, 0x4910, 0x5030, 0x5130, 0x5230, 0x5330, 0x5430, 0x5530, rlm@33: 0x5010, 0x5110, 0x5210, 0x5310, 0x5410, 0x5510, 0x5610, 0x5710, rlm@33: 0x5810, 0x5910, 0x6030, 0x6130, 0x6230, 0x6330, 0x6430, 0x6530, rlm@33: 0x0600, 0x0700, 0x0800, 0x0900, 0x0A00, 0x0B00, 0x0C00, 0x0D00, rlm@33: 0x0E00, 0x0F00, 0x1020, 0x1120, 0x1220, 0x1320, 0x1420, 0x1520, rlm@33: 0x1600, 0x1700, 0x1800, 0x1900, 0x1A00, 0x1B00, 0x1C00, 0x1D00, rlm@33: 0x1E00, 0x1F00, 0x2020, 0x2120, 0x2220, 0x2320, 0x2420, 0x2520, rlm@33: 0x2600, 0x2700, 0x2800, 0x2900, 0x2A00, 0x2B00, 0x2C00, 0x2D00, rlm@33: 0x2E00, 0x2F00, 0x3020, 0x3120, 0x3220, 0x3320, 0x3420, 0x3520, rlm@33: 0x3600, 0x3700, 0x3800, 0x3900, 0x3A00, 0x3B00, 0x3C00, 0x3D00, rlm@33: 0x3E00, 0x3F00, 0x4020, 0x4120, 0x4220, 0x4320, 0x4420, 0x4520, rlm@33: 0x4600, 0x4700, 0x4800, 0x4900, 0x4A00, 0x4B00, 0x4C00, 0x4D00, rlm@33: 0x4E00, 0x4F00, 0x5020, 0x5120, 0x5220, 0x5320, 0x5420, 0x5520, rlm@33: 0x5600, 0x5700, 0x5800, 0x5900, 0x5A00, 0x5B00, 0x5C00, 0x5D00, rlm@33: 0x5E00, 0x5F00, 0x6020, 0x6120, 0x6220, 0x6320, 0x6420, 0x6520, rlm@33: 0x6600, 0x6700, 0x6800, 0x6900, 0x6A00, 0x6B00, 0x6C00, 0x6D00, rlm@33: 0x6E00, 0x6F00, 0x7020, 0x7120, 0x7220, 0x7320, 0x7420, 0x7520, rlm@33: 0x7600, 0x7700, 0x7800, 0x7900, 0x7A00, 0x7B00, 0x7C00, 0x7D00, rlm@33: 0x7E00, 0x7F00, 0x8020, 0x8120, 0x8220, 0x8320, 0x8420, 0x8520, rlm@33: 0x8600, 0x8700, 0x8800, 0x8900, 0x8A00, 0x8B00, 0x8C00, 0x8D00, rlm@33: 0x8E00, 0x8F00, 0x9020, 0x9120, 0x9220, 0x9320, 0x9420, 0x9520, rlm@33: 0x9600, 0x9700, 0x9800, 0x9900, 0x9A00, 0x9B00, 0x9C00, 0x9D00, rlm@33: 0x9E00, 0x9F00, 0x00B0, 0x0130, 0x0230, 0x0330, 0x0430, 0x0530, rlm@33: 0x0610, 0x0710, 0x0810, 0x0910, 0x0A10, 0x0B10, 0x0C10, 0x0D10, rlm@33: 0x0E10, 0x0F10, 0x1030, 0x1130, 0x1230, 0x1330, 0x1430, 0x1530, rlm@33: 0x1610, 0x1710, 0x1810, 0x1910, 0x1A10, 0x1B10, 0x1C10, 0x1D10, rlm@33: 0x1E10, 0x1F10, 0x2030, 0x2130, 0x2230, 0x2330, 0x2430, 0x2530, rlm@33: 0x2610, 0x2710, 0x2810, 0x2910, 0x2A10, 0x2B10, 0x2C10, 0x2D10, rlm@33: 0x2E10, 0x2F10, 0x3030, 0x3130, 0x3230, 0x3330, 0x3430, 0x3530, rlm@33: 0x3610, 0x3710, 0x3810, 0x3910, 0x3A10, 0x3B10, 0x3C10, 0x3D10, rlm@33: 0x3E10, 0x3F10, 0x4030, 0x4130, 0x4230, 0x4330, 0x4430, 0x4530, rlm@33: 0x4610, 0x4710, 0x4810, 0x4910, 0x4A10, 0x4B10, 0x4C10, 0x4D10, rlm@33: 0x4E10, 0x4F10, 0x5030, 0x5130, 0x5230, 0x5330, 0x5430, 0x5530, rlm@33: 0x5610, 0x5710, 0x5810, 0x5910, 0x5A10, 0x5B10, 0x5C10, 0x5D10, rlm@33: 0x5E10, 0x5F10, 0x6030, 0x6130, 0x6230, 0x6330, 0x6430, 0x6530, rlm@33: 0x6610, 0x6710, 0x6810, 0x6910, 0x6A10, 0x6B10, 0x6C10, 0x6D10, rlm@33: 0x6E10, 0x6F10, 0x7030, 0x7130, 0x7230, 0x7330, 0x7430, 0x7530, rlm@33: 0x7610, 0x7710, 0x7810, 0x7910, 0x7A10, 0x7B10, 0x7C10, 0x7D10, rlm@33: 0x7E10, 0x7F10, 0x8030, 0x8130, 0x8230, 0x8330, 0x8430, 0x8530, rlm@33: 0x8610, 0x8710, 0x8810, 0x8910, 0x8A10, 0x8B10, 0x8C10, 0x8D10, rlm@33: 0x8E10, 0x8F10, 0x9030, 0x9130, 0x9230, 0x9330, 0x9430, 0x9530, rlm@33: 0x9610, 0x9710, 0x9810, 0x9910, 0x9A10, 0x9B10, 0x9C10, 0x9D10, rlm@33: 0x9E10, 0x9F10, 0xA030, 0xA130, 0xA230, 0xA330, 0xA430, 0xA530, rlm@33: 0xA610, 0xA710, 0xA810, 0xA910, 0xAA10, 0xAB10, 0xAC10, 0xAD10, rlm@33: 0xAE10, 0xAF10, 0xB030, 0xB130, 0xB230, 0xB330, 0xB430, 0xB530, rlm@33: 0xB610, 0xB710, 0xB810, 0xB910, 0xBA10, 0xBB10, 0xBC10, 0xBD10, rlm@33: 0xBE10, 0xBF10, 0xC030, 0xC130, 0xC230, 0xC330, 0xC430, 0xC530, rlm@33: 0xC610, 0xC710, 0xC810, 0xC910, 0xCA10, 0xCB10, 0xCC10, 0xCD10, rlm@33: 0xCE10, 0xCF10, 0xD030, 0xD130, 0xD230, 0xD330, 0xD430, 0xD530, rlm@33: 0xD610, 0xD710, 0xD810, 0xD910, 0xDA10, 0xDB10, 0xDC10, 0xDD10, rlm@33: 0xDE10, 0xDF10, 0xE030, 0xE130, 0xE230, 0xE330, 0xE430, 0xE530, rlm@33: 0xE610, 0xE710, 0xE810, 0xE910, 0xEA10, 0xEB10, 0xEC10, 0xED10, rlm@33: 0xEE10, 0xEF10, 0xF030, 0xF130, 0xF230, 0xF330, 0xF430, 0xF530, rlm@33: 0xF610, 0xF710, 0xF810, 0xF910, 0xFA10, 0xFB10, 0xFC10, 0xFD10, rlm@33: 0xFE10, 0xFF10, 0x00B0, 0x0130, 0x0230, 0x0330, 0x0430, 0x0530, rlm@33: 0x0610, 0x0710, 0x0810, 0x0910, 0x0A10, 0x0B10, 0x0C10, 0x0D10, rlm@33: 0x0E10, 0x0F10, 0x1030, 0x1130, 0x1230, 0x1330, 0x1430, 0x1530, rlm@33: 0x1610, 0x1710, 0x1810, 0x1910, 0x1A10, 0x1B10, 0x1C10, 0x1D10, rlm@33: 0x1E10, 0x1F10, 0x2030, 0x2130, 0x2230, 0x2330, 0x2430, 0x2530, rlm@33: 0x2610, 0x2710, 0x2810, 0x2910, 0x2A10, 0x2B10, 0x2C10, 0x2D10, rlm@33: 0x2E10, 0x2F10, 0x3030, 0x3130, 0x3230, 0x3330, 0x3430, 0x3530, rlm@33: 0x3610, 0x3710, 0x3810, 0x3910, 0x3A10, 0x3B10, 0x3C10, 0x3D10, rlm@33: 0x3E10, 0x3F10, 0x4030, 0x4130, 0x4230, 0x4330, 0x4430, 0x4530, rlm@33: 0x4610, 0x4710, 0x4810, 0x4910, 0x4A10, 0x4B10, 0x4C10, 0x4D10, rlm@33: 0x4E10, 0x4F10, 0x5030, 0x5130, 0x5230, 0x5330, 0x5430, 0x5530, rlm@33: 0x5610, 0x5710, 0x5810, 0x5910, 0x5A10, 0x5B10, 0x5C10, 0x5D10, rlm@33: 0x5E10, 0x5F10, 0x6030, 0x6130, 0x6230, 0x6330, 0x6430, 0x6530, rlm@33: 0x00C0, 0x0140, 0x0240, 0x0340, 0x0440, 0x0540, 0x0640, 0x0740, rlm@33: 0x0840, 0x0940, 0x0440, 0x0540, 0x0640, 0x0740, 0x0840, 0x0940, rlm@33: 0x1040, 0x1140, 0x1240, 0x1340, 0x1440, 0x1540, 0x1640, 0x1740, rlm@33: 0x1840, 0x1940, 0x1440, 0x1540, 0x1640, 0x1740, 0x1840, 0x1940, rlm@33: 0x2040, 0x2140, 0x2240, 0x2340, 0x2440, 0x2540, 0x2640, 0x2740, rlm@33: 0x2840, 0x2940, 0x2440, 0x2540, 0x2640, 0x2740, 0x2840, 0x2940, rlm@33: 0x3040, 0x3140, 0x3240, 0x3340, 0x3440, 0x3540, 0x3640, 0x3740, rlm@33: 0x3840, 0x3940, 0x3440, 0x3540, 0x3640, 0x3740, 0x3840, 0x3940, rlm@33: 0x4040, 0x4140, 0x4240, 0x4340, 0x4440, 0x4540, 0x4640, 0x4740, rlm@33: 0x4840, 0x4940, 0x4440, 0x4540, 0x4640, 0x4740, 0x4840, 0x4940, rlm@33: 0x5040, 0x5140, 0x5240, 0x5340, 0x5440, 0x5540, 0x5640, 0x5740, rlm@33: 0x5840, 0x5940, 0x5440, 0x5540, 0x5640, 0x5740, 0x5840, 0x5940, rlm@33: 0x6040, 0x6140, 0x6240, 0x6340, 0x6440, 0x6540, 0x6640, 0x6740, rlm@33: 0x6840, 0x6940, 0x6440, 0x6540, 0x6640, 0x6740, 0x6840, 0x6940, rlm@33: 0x7040, 0x7140, 0x7240, 0x7340, 0x7440, 0x7540, 0x7640, 0x7740, rlm@33: 0x7840, 0x7940, 0x7440, 0x7540, 0x7640, 0x7740, 0x7840, 0x7940, rlm@33: 0x8040, 0x8140, 0x8240, 0x8340, 0x8440, 0x8540, 0x8640, 0x8740, rlm@33: 0x8840, 0x8940, 0x8440, 0x8540, 0x8640, 0x8740, 0x8840, 0x8940, rlm@33: 0x9040, 0x9140, 0x9240, 0x9340, 0x9440, 0x9540, 0x9640, 0x9740, rlm@33: 0x9840, 0x9940, 0x3450, 0x3550, 0x3650, 0x3750, 0x3850, 0x3950, rlm@33: 0x4050, 0x4150, 0x4250, 0x4350, 0x4450, 0x4550, 0x4650, 0x4750, rlm@33: 0x4850, 0x4950, 0x4450, 0x4550, 0x4650, 0x4750, 0x4850, 0x4950, rlm@33: 0x5050, 0x5150, 0x5250, 0x5350, 0x5450, 0x5550, 0x5650, 0x5750, rlm@33: 0x5850, 0x5950, 0x5450, 0x5550, 0x5650, 0x5750, 0x5850, 0x5950, rlm@33: 0x6050, 0x6150, 0x6250, 0x6350, 0x6450, 0x6550, 0x6650, 0x6750, rlm@33: 0x6850, 0x6950, 0x6450, 0x6550, 0x6650, 0x6750, 0x6850, 0x6950, rlm@33: 0x7050, 0x7150, 0x7250, 0x7350, 0x7450, 0x7550, 0x7650, 0x7750, rlm@33: 0x7850, 0x7950, 0x7450, 0x7550, 0x7650, 0x7750, 0x7850, 0x7950, rlm@33: 0x8050, 0x8150, 0x8250, 0x8350, 0x8450, 0x8550, 0x8650, 0x8750, rlm@33: 0x8850, 0x8950, 0x8450, 0x8550, 0x8650, 0x8750, 0x8850, 0x8950, rlm@33: 0x9050, 0x9150, 0x9250, 0x9350, 0x9450, 0x9550, 0x9650, 0x9750, rlm@33: 0x9850, 0x9950, 0x9450, 0x9550, 0x9650, 0x9750, 0x9850, 0x9950, rlm@33: 0xA050, 0xA150, 0xA250, 0xA350, 0xA450, 0xA550, 0xA650, 0xA750, rlm@33: 0xA850, 0xA950, 0xA450, 0xA550, 0xA650, 0xA750, 0xA850, 0xA950, rlm@33: 0xB050, 0xB150, 0xB250, 0xB350, 0xB450, 0xB550, 0xB650, 0xB750, rlm@33: 0xB850, 0xB950, 0xB450, 0xB550, 0xB650, 0xB750, 0xB850, 0xB950, rlm@33: 0xC050, 0xC150, 0xC250, 0xC350, 0xC450, 0xC550, 0xC650, 0xC750, rlm@33: 0xC850, 0xC950, 0xC450, 0xC550, 0xC650, 0xC750, 0xC850, 0xC950, rlm@33: 0xD050, 0xD150, 0xD250, 0xD350, 0xD450, 0xD550, 0xD650, 0xD750, rlm@33: 0xD850, 0xD950, 0xD450, 0xD550, 0xD650, 0xD750, 0xD850, 0xD950, rlm@33: 0xE050, 0xE150, 0xE250, 0xE350, 0xE450, 0xE550, 0xE650, 0xE750, rlm@33: 0xE850, 0xE950, 0xE450, 0xE550, 0xE650, 0xE750, 0xE850, 0xE950, rlm@33: 0xF050, 0xF150, 0xF250, 0xF350, 0xF450, 0xF550, 0xF650, 0xF750, rlm@33: 0xF850, 0xF950, 0xF450, 0xF550, 0xF650, 0xF750, 0xF850, 0xF950, rlm@33: 0x00D0, 0x0150, 0x0250, 0x0350, 0x0450, 0x0550, 0x0650, 0x0750, rlm@33: 0x0850, 0x0950, 0x0450, 0x0550, 0x0650, 0x0750, 0x0850, 0x0950, rlm@33: 0x1050, 0x1150, 0x1250, 0x1350, 0x1450, 0x1550, 0x1650, 0x1750, rlm@33: 0x1850, 0x1950, 0x1450, 0x1550, 0x1650, 0x1750, 0x1850, 0x1950, rlm@33: 0x2050, 0x2150, 0x2250, 0x2350, 0x2450, 0x2550, 0x2650, 0x2750, rlm@33: 0x2850, 0x2950, 0x2450, 0x2550, 0x2650, 0x2750, 0x2850, 0x2950, rlm@33: 0x3050, 0x3150, 0x3250, 0x3350, 0x3450, 0x3550, 0x3650, 0x3750, rlm@33: 0x3850, 0x3950, 0x3450, 0x3550, 0x3650, 0x3750, 0x3850, 0x3950, rlm@33: 0x4050, 0x4150, 0x4250, 0x4350, 0x4450, 0x4550, 0x4650, 0x4750, rlm@33: 0x4850, 0x4950, 0x4450, 0x4550, 0x4650, 0x4750, 0x4850, 0x4950, rlm@33: 0x5050, 0x5150, 0x5250, 0x5350, 0x5450, 0x5550, 0x5650, 0x5750, rlm@33: 0x5850, 0x5950, 0x5450, 0x5550, 0x5650, 0x5750, 0x5850, 0x5950, rlm@33: 0x6050, 0x6150, 0x6250, 0x6350, 0x6450, 0x6550, 0x6650, 0x6750, rlm@33: 0x6850, 0x6950, 0x6450, 0x6550, 0x6650, 0x6750, 0x6850, 0x6950, rlm@33: 0x7050, 0x7150, 0x7250, 0x7350, 0x7450, 0x7550, 0x7650, 0x7750, rlm@33: 0x7850, 0x7950, 0x7450, 0x7550, 0x7650, 0x7750, 0x7850, 0x7950, rlm@33: 0x8050, 0x8150, 0x8250, 0x8350, 0x8450, 0x8550, 0x8650, 0x8750, rlm@33: 0x8850, 0x8950, 0x8450, 0x8550, 0x8650, 0x8750, 0x8850, 0x8950, rlm@33: 0x9050, 0x9150, 0x9250, 0x9350, 0x9450, 0x9550, 0x9650, 0x9750, rlm@33: 0x9850, 0x9950, 0x9450, 0x9550, 0x9650, 0x9750, 0x9850, 0x9950, rlm@33: 0xFA60, 0xFB60, 0xFC60, 0xFD60, 0xFE60, 0xFF60, 0x00C0, 0x0140, rlm@33: 0x0240, 0x0340, 0x0440, 0x0540, 0x0640, 0x0740, 0x0840, 0x0940, rlm@33: 0x0A60, 0x0B60, 0x0C60, 0x0D60, 0x0E60, 0x0F60, 0x1040, 0x1140, rlm@33: 0x1240, 0x1340, 0x1440, 0x1540, 0x1640, 0x1740, 0x1840, 0x1940, rlm@33: 0x1A60, 0x1B60, 0x1C60, 0x1D60, 0x1E60, 0x1F60, 0x2040, 0x2140, rlm@33: 0x2240, 0x2340, 0x2440, 0x2540, 0x2640, 0x2740, 0x2840, 0x2940, rlm@33: 0x2A60, 0x2B60, 0x2C60, 0x2D60, 0x2E60, 0x2F60, 0x3040, 0x3140, rlm@33: 0x3240, 0x3340, 0x3440, 0x3540, 0x3640, 0x3740, 0x3840, 0x3940, rlm@33: 0x3A60, 0x3B60, 0x3C60, 0x3D60, 0x3E60, 0x3F60, 0x4040, 0x4140, rlm@33: 0x4240, 0x4340, 0x4440, 0x4540, 0x4640, 0x4740, 0x4840, 0x4940, rlm@33: 0x4A60, 0x4B60, 0x4C60, 0x4D60, 0x4E60, 0x4F60, 0x5040, 0x5140, rlm@33: 0x5240, 0x5340, 0x5440, 0x5540, 0x5640, 0x5740, 0x5840, 0x5940, rlm@33: 0x5A60, 0x5B60, 0x5C60, 0x5D60, 0x5E60, 0x5F60, 0x6040, 0x6140, rlm@33: 0x6240, 0x6340, 0x6440, 0x6540, 0x6640, 0x6740, 0x6840, 0x6940, rlm@33: 0x6A60, 0x6B60, 0x6C60, 0x6D60, 0x6E60, 0x6F60, 0x7040, 0x7140, rlm@33: 0x7240, 0x7340, 0x7440, 0x7540, 0x7640, 0x7740, 0x7840, 0x7940, rlm@33: 0x7A60, 0x7B60, 0x7C60, 0x7D60, 0x7E60, 0x7F60, 0x8040, 0x8140, rlm@33: 0x8240, 0x8340, 0x8440, 0x8540, 0x8640, 0x8740, 0x8840, 0x8940, rlm@33: 0x8A60, 0x8B60, 0x8C60, 0x8D60, 0x8E60, 0x8F60, 0x9040, 0x9140, rlm@33: 0x9240, 0x9340, 0x3450, 0x3550, 0x3650, 0x3750, 0x3850, 0x3950, rlm@33: 0x3A70, 0x3B70, 0x3C70, 0x3D70, 0x3E70, 0x3F70, 0x4050, 0x4150, rlm@33: 0x4250, 0x4350, 0x4450, 0x4550, 0x4650, 0x4750, 0x4850, 0x4950, rlm@33: 0x4A70, 0x4B70, 0x4C70, 0x4D70, 0x4E70, 0x4F70, 0x5050, 0x5150, rlm@33: 0x5250, 0x5350, 0x5450, 0x5550, 0x5650, 0x5750, 0x5850, 0x5950, rlm@33: 0x5A70, 0x5B70, 0x5C70, 0x5D70, 0x5E70, 0x5F70, 0x6050, 0x6150, rlm@33: 0x6250, 0x6350, 0x6450, 0x6550, 0x6650, 0x6750, 0x6850, 0x6950, rlm@33: 0x6A70, 0x6B70, 0x6C70, 0x6D70, 0x6E70, 0x6F70, 0x7050, 0x7150, rlm@33: 0x7250, 0x7350, 0x7450, 0x7550, 0x7650, 0x7750, 0x7850, 0x7950, rlm@33: 0x7A70, 0x7B70, 0x7C70, 0x7D70, 0x7E70, 0x7F70, 0x8050, 0x8150, rlm@33: 0x8250, 0x8350, 0x8450, 0x8550, 0x8650, 0x8750, 0x8850, 0x8950, rlm@33: 0x8A70, 0x8B70, 0x8C70, 0x8D70, 0x8E70, 0x8F70, 0x9050, 0x9150, rlm@33: 0x9250, 0x9350, 0x9450, 0x9550, 0x9650, 0x9750, 0x9850, 0x9950, rlm@33: 0x9A70, 0x9B70, 0x9C70, 0x9D70, 0x9E70, 0x9F70, 0xA050, 0xA150, rlm@33: 0xA250, 0xA350, 0xA450, 0xA550, 0xA650, 0xA750, 0xA850, 0xA950, rlm@33: 0xAA70, 0xAB70, 0xAC70, 0xAD70, 0xAE70, 0xAF70, 0xB050, 0xB150, rlm@33: 0xB250, 0xB350, 0xB450, 0xB550, 0xB650, 0xB750, 0xB850, 0xB950, rlm@33: 0xBA70, 0xBB70, 0xBC70, 0xBD70, 0xBE70, 0xBF70, 0xC050, 0xC150, rlm@33: 0xC250, 0xC350, 0xC450, 0xC550, 0xC650, 0xC750, 0xC850, 0xC950, rlm@33: 0xCA70, 0xCB70, 0xCC70, 0xCD70, 0xCE70, 0xCF70, 0xD050, 0xD150, rlm@33: 0xD250, 0xD350, 0xD450, 0xD550, 0xD650, 0xD750, 0xD850, 0xD950, rlm@33: 0xDA70, 0xDB70, 0xDC70, 0xDD70, 0xDE70, 0xDF70, 0xE050, 0xE150, rlm@33: 0xE250, 0xE350, 0xE450, 0xE550, 0xE650, 0xE750, 0xE850, 0xE950, rlm@33: 0xEA70, 0xEB70, 0xEC70, 0xED70, 0xEE70, 0xEF70, 0xF050, 0xF150, rlm@33: 0xF250, 0xF350, 0xF450, 0xF550, 0xF650, 0xF750, 0xF850, 0xF950, rlm@33: 0xFA70, 0xFB70, 0xFC70, 0xFD70, 0xFE70, 0xFF70, 0x00D0, 0x0150, rlm@33: 0x0250, 0x0350, 0x0450, 0x0550, 0x0650, 0x0750, 0x0850, 0x0950, rlm@33: 0x0A70, 0x0B70, 0x0C70, 0x0D70, 0x0E70, 0x0F70, 0x1050, 0x1150, rlm@33: 0x1250, 0x1350, 0x1450, 0x1550, 0x1650, 0x1750, 0x1850, 0x1950, rlm@33: 0x1A70, 0x1B70, 0x1C70, 0x1D70, 0x1E70, 0x1F70, 0x2050, 0x2150, rlm@33: 0x2250, 0x2350, 0x2450, 0x2550, 0x2650, 0x2750, 0x2850, 0x2950, rlm@33: 0x2A70, 0x2B70, 0x2C70, 0x2D70, 0x2E70, 0x2F70, 0x3050, 0x3150, rlm@33: 0x3250, 0x3350, 0x3450, 0x3550, 0x3650, 0x3750, 0x3850, 0x3950, rlm@33: 0x3A70, 0x3B70, 0x3C70, 0x3D70, 0x3E70, 0x3F70, 0x4050, 0x4150, rlm@33: 0x4250, 0x4350, 0x4450, 0x4550, 0x4650, 0x4750, 0x4850, 0x4950, rlm@33: 0x4A70, 0x4B70, 0x4C70, 0x4D70, 0x4E70, 0x4F70, 0x5050, 0x5150, rlm@33: 0x5250, 0x5350, 0x5450, 0x5550, 0x5650, 0x5750, 0x5850, 0x5950, rlm@33: 0x5A70, 0x5B70, 0x5C70, 0x5D70, 0x5E70, 0x5F70, 0x6050, 0x6150, rlm@33: 0x6250, 0x6350, 0x6450, 0x6550, 0x6650, 0x6750, 0x6850, 0x6950, rlm@33: 0x6A70, 0x6B70, 0x6C70, 0x6D70, 0x6E70, 0x6F70, 0x7050, 0x7150, rlm@33: 0x7250, 0x7350, 0x7450, 0x7550, 0x7650, 0x7750, 0x7850, 0x7950, rlm@33: 0x7A70, 0x7B70, 0x7C70, 0x7D70, 0x7E70, 0x7F70, 0x8050, 0x8150, rlm@33: 0x8250, 0x8350, 0x8450, 0x8550, 0x8650, 0x8750, 0x8850, 0x8950, rlm@33: 0x8A70, 0x8B70, 0x8C70, 0x8D70, 0x8E70, 0x8F70, 0x9050, 0x9150, rlm@33: 0x9250, 0x9350, 0x9450, 0x9550, 0x9650, 0x9750, 0x9850, 0x9950, rlm@33: }; rlm@1: rlm@1: u8 ZeroTable[] = rlm@33: { rlm@33: 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, rlm@33: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, rlm@33: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, rlm@33: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, rlm@33: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, rlm@33: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, rlm@33: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, rlm@33: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, rlm@33: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, rlm@33: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, rlm@33: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, rlm@33: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, rlm@33: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, rlm@33: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, rlm@33: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, rlm@33: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 rlm@33: }; rlm@1: rlm@1: #define GBSAVE_GAME_VERSION_1 1 rlm@1: #define GBSAVE_GAME_VERSION_2 2 rlm@1: #define GBSAVE_GAME_VERSION_3 3 rlm@1: #define GBSAVE_GAME_VERSION_4 4 rlm@1: #define GBSAVE_GAME_VERSION_5 5 rlm@1: #define GBSAVE_GAME_VERSION_6 6 rlm@1: #define GBSAVE_GAME_VERSION_7 7 rlm@1: #define GBSAVE_GAME_VERSION_8 8 rlm@1: #define GBSAVE_GAME_VERSION_9 9 rlm@1: #define GBSAVE_GAME_VERSION_10 10 rlm@1: #define GBSAVE_GAME_VERSION_11 11 rlm@1: #define GBSAVE_GAME_VERSION_12 12 rlm@1: #define GBSAVE_GAME_VERSION_13 13 rlm@1: #define GBSAVE_GAME_VERSION GBSAVE_GAME_VERSION_13 rlm@1: rlm@1: int inline gbGetValue(int min, int max, int v) rlm@1: { rlm@33: return (int)(min + (float)(max - min) * (2.0 * (v / 31.0) - (v / 31.0) * (v / 31.0))); rlm@1: } rlm@1: rlm@1: void gbGenFilter() rlm@1: { rlm@33: for (int r = 0; r < 32; r++) rlm@33: { rlm@33: for (int g = 0; g < 32; g++) rlm@1: { rlm@33: for (int b = 0; b < 32; b++) rlm@33: { rlm@33: int nr = gbGetValue(gbGetValue(4, 14, g), rlm@33: gbGetValue(24, 29, g), r) - 4; rlm@33: int ng = gbGetValue(gbGetValue(4 + gbGetValue(0, 5, r), rlm@33: 14 + gbGetValue(0, 3, r), b), rlm@33: gbGetValue(24 + gbGetValue(0, 3, r), rlm@33: 29 + gbGetValue(0, 1, r), b), g) - 4; rlm@33: int nb = gbGetValue(gbGetValue(4 + gbGetValue(0, 5, r), rlm@33: 14 + gbGetValue(0, 3, r), g), rlm@33: gbGetValue(24 + gbGetValue(0, 3, r), rlm@33: 29 + gbGetValue(0, 1, r), g), b) - 4; rlm@33: gbColorFilter[(b << 10) | (g << 5) | r] = (nb << 10) | (ng << 5) | nr; rlm@33: } rlm@1: } rlm@33: } rlm@1: } rlm@1: rlm@1: void gbCopyMemory(u16 d, u16 s, int count) rlm@1: { rlm@33: while (count) rlm@33: { rlm@33: gbWriteMemoryQuick(d, gbReadMemoryQuick(s)); rlm@33: s++; rlm@33: d++; rlm@33: count--; rlm@33: } rlm@1: } rlm@1: rlm@1: void gbDoHdma() rlm@1: { rlm@33: gbCopyMemory(gbHdmaDestination, gbHdmaSource, 0x10); rlm@33: rlm@33: gbHdmaDestination += 0x10; rlm@33: gbHdmaSource += 0x10; rlm@33: rlm@33: register_HDMA2 = (register_HDMA2 + 0x10) & 0xFF; rlm@33: if (register_HDMA2 == 0x00) rlm@33: register_HDMA1++; rlm@33: rlm@33: register_HDMA4 = (register_HDMA4 + 0x10) & 0xFF; rlm@33: if (register_HDMA4 == 0x00) rlm@33: register_HDMA3++; rlm@33: rlm@33: if (gbHdmaDestination == 0x96b0) rlm@33: gbHdmaBytes = gbHdmaBytes; rlm@33: gbHdmaBytes -= 0x10; rlm@33: register_HDMA5--; rlm@33: if (register_HDMA5 == 0xff) rlm@33: gbHdmaOn = 0; rlm@1: } rlm@1: rlm@1: // fix for Harley and Lego Racers rlm@1: void gbCompareLYToLYC() rlm@1: { rlm@33: if (register_LY == register_LYC) rlm@33: { rlm@33: // mark that we have a match rlm@33: register_STAT |= 4; rlm@33: rlm@33: // check if we need an interrupt rlm@33: if ((register_STAT & 0x40) && (register_IE & 2)) rlm@33: gbInterrupt |= 2; rlm@33: } rlm@33: else // no match rlm@33: register_STAT &= 0xfb; rlm@1: } rlm@1: rlm@1: // FIXME: horrible kludge to workaround the frame timing bug rlm@1: static int32 s_gbJoymask[4] = { 0, 0, 0, 0 }; rlm@1: rlm@1: void gbWriteMemoryWrapped(register u16 address, register u8 value) rlm@1: { rlm@33: if (address < 0x8000) rlm@33: { rlm@33: #ifndef FINAL_VERSION rlm@33: if (memorydebug && (address > 0x3fff || address < 0x2000)) rlm@1: { rlm@33: log("Memory register write %04x=%02x PC=%04x\n", rlm@33: address, rlm@33: value, rlm@33: PC.W); rlm@33: } rlm@33: #endif rlm@33: if (mapper) rlm@33: (*mapper)(address, value); rlm@33: return; rlm@33: } rlm@33: rlm@33: if (address < 0xa000) rlm@33: { rlm@33: gbWriteMemoryQuick(address, value); rlm@33: return; rlm@33: } rlm@33: rlm@33: if (address < 0xc000) rlm@33: { rlm@1: #ifndef FINAL_VERSION rlm@33: if (memorydebug) rlm@33: { rlm@33: log("Memory register write %04x=%02x PC=%04x\n", rlm@33: address, rlm@33: value, rlm@33: PC.W); rlm@33: } rlm@1: #endif rlm@33: rlm@33: if (mapper) rlm@33: (*mapperRAM)(address, value); rlm@33: return; rlm@33: } rlm@33: rlm@33: if (address < 0xfe00) rlm@33: { rlm@33: gbWriteMemoryQuick(address, value); rlm@33: return; rlm@33: } rlm@33: rlm@33: if (address < 0xff00) rlm@33: { rlm@33: gbMemory[address] = value; rlm@33: return; rlm@33: } rlm@33: rlm@33: switch (address & 0x00ff) rlm@33: { rlm@33: case 0x00: rlm@33: { rlm@33: gbMemory[0xff00] = ((gbMemory[0xff00] & 0xcf) | rlm@33: (value & 0x30)); rlm@33: if (gbSgbMode) rlm@33: { rlm@33: gbSgbDoBitTransfer(value); rlm@33: } rlm@33: rlm@33: return; rlm@33: } rlm@33: rlm@33: case 0x01: rlm@33: { rlm@33: gbMemory[0xff01] = value; rlm@33: return; rlm@33: } rlm@33: rlm@33: // serial control rlm@33: case 0x02: rlm@33: { rlm@33: gbSerialOn = (value & 0x80); rlm@33: gbMemory[0xff02] = value; rlm@33: if (gbSerialOn) rlm@33: { rlm@33: gbSerialTicks = GBSERIAL_CLOCK_TICKS; rlm@33: #ifdef LINK_EMULATION rlm@33: if (linkConnected) rlm@33: { rlm@33: if (value & 1) rlm@33: { rlm@33: linkSendByte(0x100 | gbMemory[0xFF01]); rlm@33: Sleep(5); rlm@33: } rlm@33: } rlm@33: #endif rlm@33: } rlm@33: rlm@33: gbSerialBits = 0; rlm@33: return; rlm@33: } rlm@33: rlm@33: // DIV register resets on any write rlm@33: case 0x04: rlm@33: { rlm@33: register_DIV = 0; rlm@33: return; rlm@33: } rlm@33: case 0x05: rlm@33: register_TIMA = value; rlm@33: return; rlm@33: rlm@33: case 0x06: rlm@33: register_TMA = value; rlm@33: return; rlm@33: rlm@33: // TIMER control rlm@33: case 0x07: rlm@33: { rlm@33: register_TAC = value; rlm@33: rlm@33: gbTimerOn = (value & 4); rlm@33: gbTimerMode = value & 3; rlm@33: // register_TIMA = register_TMA; rlm@33: switch (gbTimerMode) rlm@33: { rlm@33: case 0: rlm@33: gbTimerClockTicks = gbTimerTicks = GBTIMER_MODE_0_CLOCK_TICKS; rlm@33: break; rlm@33: case 1: rlm@33: gbTimerClockTicks = gbTimerTicks = GBTIMER_MODE_1_CLOCK_TICKS; rlm@33: break; rlm@33: case 2: rlm@33: gbTimerClockTicks = gbTimerTicks = GBTIMER_MODE_2_CLOCK_TICKS; rlm@33: break; rlm@33: case 3: rlm@33: gbTimerClockTicks = gbTimerTicks = GBTIMER_MODE_3_CLOCK_TICKS; rlm@33: break; rlm@33: } rlm@33: return; rlm@33: } rlm@33: rlm@33: case 0x0f: rlm@33: { rlm@33: register_IF = value; rlm@33: gbInterrupt = value; rlm@33: return; rlm@33: } rlm@33: rlm@33: case 0x10: rlm@33: case 0x11: rlm@33: case 0x12: rlm@33: case 0x13: rlm@33: case 0x14: rlm@33: case 0x15: rlm@33: case 0x16: rlm@33: case 0x17: rlm@33: case 0x18: rlm@33: case 0x19: rlm@33: case 0x1a: rlm@33: case 0x1b: rlm@33: case 0x1c: rlm@33: case 0x1d: rlm@33: case 0x1e: rlm@33: case 0x1f: rlm@33: case 0x20: rlm@33: case 0x21: rlm@33: case 0x22: rlm@33: case 0x23: rlm@33: case 0x24: rlm@33: case 0x25: rlm@33: case 0x26: rlm@33: { rlm@33: SOUND_EVENT(address, value); rlm@33: return; rlm@33: } rlm@33: case 0x40: rlm@33: { rlm@33: int lcdChange = (register_LCDC & 0x80) ^ (value & 0x80); rlm@33: rlm@33: if (lcdChange) rlm@33: { rlm@33: if (value & 0x80) rlm@33: { rlm@33: gbLcdTicks = GBLCD_MODE_1_CLOCK_TICKS; rlm@33: gbLcdMode = 0; rlm@33: register_STAT &= 0xfc; rlm@33: register_LY = 0x00; rlm@33: // FIXME: horrible workaround rlm@33: if (gbNullInputHackTempEnabled && !useOldFrameTiming) rlm@33: memcpy(gbJoymask, s_gbJoymask, sizeof(gbJoymask)); rlm@33: } rlm@33: else rlm@33: { rlm@33: gbLcdTicks = 0; rlm@33: gbLcdMode = 0; rlm@33: register_STAT &= 0xfc; rlm@33: register_LY = 0x00; rlm@33: // FIXME: horrible workaround rlm@33: memcpy(s_gbJoymask, gbJoymask, sizeof(gbJoymask)); rlm@33: if (gbNullInputHackTempEnabled && !useOldFrameTiming) rlm@33: memset(gbJoymask, 0, sizeof(gbJoymask)); rlm@33: } rlm@33: // compareLYToLYC(); rlm@33: } rlm@33: // don't draw the window if it was not enabled and not being drawn before rlm@33: if (!(register_LCDC & 0x20) && (value & 0x20) && gbWindowLine == -1 && rlm@33: register_LY > register_WY) rlm@33: gbWindowLine = 144; rlm@33: rlm@33: register_LCDC = value; rlm@33: rlm@33: return; rlm@33: } rlm@33: rlm@33: // STAT rlm@33: case 0x41: rlm@33: { rlm@33: //register_STAT = (register_STAT & 0x87) | rlm@33: // (value & 0x7c); rlm@33: register_STAT = (value & 0xf8) | (register_STAT & 0x07); // fix ? rlm@33: // GB bug from Devrs FAQ rlm@33: if (!gbCgbMode && (register_LCDC & 0x80) && gbLcdMode < 2) rlm@33: gbInterrupt |= 2; rlm@33: return; rlm@33: } rlm@33: rlm@33: // SCY rlm@33: case 0x42: rlm@33: { rlm@33: register_SCY = value; rlm@33: return; rlm@33: } rlm@33: rlm@33: // SCX rlm@33: case 0x43: rlm@33: { rlm@33: register_SCX = value; rlm@33: return; rlm@33: } rlm@33: rlm@33: // LY rlm@33: case 0x44: rlm@33: { rlm@33: // read only rlm@33: return; rlm@33: } rlm@33: rlm@33: // LYC rlm@33: case 0x45: rlm@33: { rlm@33: register_LYC = value; rlm@33: if ((register_LCDC & 0x80)) rlm@33: { rlm@33: gbCompareLYToLYC(); rlm@33: } rlm@33: return; rlm@33: } rlm@33: rlm@33: // DMA! rlm@33: case 0x46: rlm@33: { rlm@33: int source = value * 0x0100; rlm@33: rlm@33: gbCopyMemory(0xfe00, rlm@33: source, rlm@33: 0xa0); rlm@33: register_DMA = value; rlm@33: return; rlm@33: } rlm@33: rlm@33: // BGP rlm@33: case 0x47: rlm@33: { rlm@33: gbBgp[0] = value & 0x03; rlm@33: gbBgp[1] = (value & 0x0c) >> 2; rlm@33: gbBgp[2] = (value & 0x30) >> 4; rlm@33: gbBgp[3] = (value & 0xc0) >> 6; rlm@33: break; rlm@33: } rlm@33: rlm@33: // OBP0 rlm@33: case 0x48: rlm@33: { rlm@33: gbObp0[0] = value & 0x03; rlm@33: gbObp0[1] = (value & 0x0c) >> 2; rlm@33: gbObp0[2] = (value & 0x30) >> 4; rlm@33: gbObp0[3] = (value & 0xc0) >> 6; rlm@33: break; rlm@33: } rlm@33: rlm@33: // OBP1 rlm@33: case 0x49: rlm@33: { rlm@33: gbObp1[0] = value & 0x03; rlm@33: gbObp1[1] = (value & 0x0c) >> 2; rlm@33: gbObp1[2] = (value & 0x30) >> 4; rlm@33: gbObp1[3] = (value & 0xc0) >> 6; rlm@33: break; rlm@33: } rlm@33: rlm@33: case 0x4a: rlm@33: register_WY = value; rlm@33: return; rlm@33: rlm@33: case 0x4b: rlm@33: register_WX = value; rlm@33: return; rlm@33: rlm@33: // KEY1 rlm@33: case 0x4d: rlm@33: { rlm@33: if (gbCgbMode) rlm@33: { rlm@33: gbMemory[0xff4d] = (gbMemory[0xff4d] & 0x80) | (value & 1); rlm@33: return; rlm@33: } rlm@33: break; rlm@33: } rlm@33: rlm@33: // VBK rlm@33: case 0x4f: rlm@33: { rlm@33: if (gbCgbMode) rlm@33: { rlm@33: value = value & 1; rlm@33: if (value == gbVramBank) rlm@33: return; rlm@33: rlm@33: int vramAddress = value * 0x2000; rlm@33: gbMemoryMap[0x08] = &gbVram[vramAddress]; rlm@33: gbMemoryMap[0x09] = &gbVram[vramAddress + 0x1000]; rlm@33: rlm@33: gbVramBank = value; rlm@33: register_VBK = value; rlm@33: } rlm@33: return; rlm@33: break; rlm@33: } rlm@33: rlm@33: // HDMA1 rlm@33: case 0x51: rlm@33: { rlm@33: if (gbCgbMode) rlm@33: { rlm@33: if (value > 0x7f && value < 0xa0) rlm@33: value = 0; rlm@33: rlm@33: gbHdmaSource = (value << 8) | (register_HDMA2 & 0xf0); rlm@33: rlm@33: register_HDMA1 = value; rlm@33: return; rlm@33: } rlm@33: break; rlm@33: } rlm@33: rlm@33: // HDMA2 rlm@33: case 0x52: rlm@33: { rlm@33: if (gbCgbMode) rlm@33: { rlm@33: value = value & 0xf0; rlm@33: rlm@33: gbHdmaSource = (register_HDMA1 << 8) | (value); rlm@33: rlm@33: register_HDMA2 = value; rlm@33: return; rlm@33: } rlm@33: break; rlm@33: } rlm@33: rlm@33: // HDMA3 rlm@33: case 0x53: rlm@33: { rlm@33: if (gbCgbMode) rlm@33: { rlm@33: value = value & 0x1f; rlm@33: gbHdmaDestination = (value << 8) | (register_HDMA4 & 0xf0); rlm@33: gbHdmaDestination += 0x8000; rlm@33: register_HDMA3 = value; rlm@33: return; rlm@33: } rlm@33: break; rlm@33: } rlm@33: rlm@33: // HDMA4 rlm@33: case 0x54: rlm@33: { rlm@33: if (gbCgbMode) rlm@33: { rlm@33: value = value & 0xf0; rlm@33: gbHdmaDestination = ((register_HDMA3 & 0x1f) << 8) | value; rlm@33: gbHdmaDestination += 0x8000; rlm@33: register_HDMA4 = value; rlm@33: return; rlm@33: } rlm@33: break; rlm@33: } rlm@33: rlm@33: // HDMA5 rlm@33: case 0x55: rlm@33: { rlm@33: if (gbCgbMode) rlm@33: { rlm@33: gbHdmaBytes = 16 + (value & 0x7f) * 16; rlm@33: if (gbHdmaOn) rlm@33: { rlm@33: if (value & 0x80) rlm@33: { rlm@33: register_HDMA5 = (value & 0x7f); rlm@33: } rlm@33: else rlm@33: { rlm@33: register_HDMA5 = 0xff; rlm@33: gbHdmaOn = 0; rlm@33: } rlm@33: } rlm@33: else rlm@33: { rlm@33: if (value & 0x80) rlm@33: { rlm@33: gbHdmaOn = 1; rlm@33: register_HDMA5 = value & 0x7f; rlm@33: if (gbLcdMode == 0) rlm@33: gbDoHdma(); rlm@33: } rlm@33: else rlm@33: { rlm@33: // we need to take the time it takes to complete the transfer into rlm@33: // account... according to GB DEV FAQs, the setup time is the same rlm@33: // for single and double speed, but the actual transfer takes the rlm@33: // same time // (is that a typo?) rlm@33: switch (gbDMASpeedVersion) rlm@33: { rlm@33: case 1: // I believe this is more correct rlm@33: // the lower 7 bits of FF55 specify the Transfer Length (divided by 16, minus 1) rlm@33: // and we make gbDmaTicks twice as many cycles at double speed to make the transfer take the same time rlm@33: if (gbSpeed) rlm@33: gbDmaTicks = 16 * ((value & 0x7f) + 1); rlm@33: else rlm@33: gbDmaTicks = 8 * ((value & 0x7f) + 1); rlm@33: break; rlm@33: case 0: // here for backward compatibility rlm@33: // I think this was a guess that approximates the above in most but not all games rlm@33: if (gbSpeed) rlm@33: gbDmaTicks = 231 + 16 * (value & 0x7f); rlm@33: else rlm@33: gbDmaTicks = 231 + 8 * (value & 0x7f); rlm@33: break; rlm@33: default: // shouldn't happen rlm@33: //assert(0); rlm@33: break; rlm@33: } rlm@33: gbCopyMemory(gbHdmaDestination, gbHdmaSource, gbHdmaBytes); rlm@33: gbHdmaDestination += gbHdmaBytes; rlm@33: gbHdmaSource += gbHdmaBytes; rlm@33: rlm@33: register_HDMA3 = ((gbHdmaDestination - 0x8000) >> 8) & 0x1f; rlm@33: register_HDMA4 = gbHdmaDestination & 0xf0; rlm@33: register_HDMA1 = (gbHdmaSource >> 8) & 0xff; rlm@33: register_HDMA2 = gbHdmaSource & 0xf0; rlm@33: } rlm@33: } rlm@33: return; rlm@33: } rlm@33: break; rlm@33: } rlm@33: rlm@33: // BCPS rlm@33: case 0x68: rlm@33: { rlm@33: if (gbCgbMode) rlm@33: { rlm@33: int paletteIndex = (value & 0x3f) >> 1; rlm@33: int paletteHiLo = (value & 0x01); rlm@33: rlm@33: gbMemory[0xff68] = value; rlm@33: gbMemory[0xff69] = (paletteHiLo ? rlm@33: (gbPalette[paletteIndex] >> 8) : rlm@33: (gbPalette[paletteIndex] & 0x00ff)); rlm@33: return; rlm@33: } rlm@33: break; rlm@33: } rlm@33: rlm@33: // BCPD rlm@33: case 0x69: rlm@33: { rlm@33: if (gbCgbMode) rlm@33: { rlm@33: int v = gbMemory[0xff68]; rlm@33: int paletteIndex = (v & 0x3f) >> 1; rlm@33: int paletteHiLo = (v & 0x01); rlm@33: gbMemory[0xff69] = value; rlm@33: gbPalette[paletteIndex] = (paletteHiLo ? rlm@33: ((value << 8) | (gbPalette[paletteIndex] & 0xff)) : rlm@33: ((gbPalette[paletteIndex] & 0xff00) | (value))) & 0x7fff; rlm@33: rlm@33: if (gbMemory[0xff68] & 0x80) rlm@33: { rlm@33: int index = ((gbMemory[0xff68] & 0x3f) + 1) & 0x3f; rlm@33: rlm@33: gbMemory[0xff68] = (gbMemory[0xff68] & 0x80) | index; rlm@33: rlm@33: gbMemory[0xff69] = (index & 1 ? rlm@33: (gbPalette[index >> 1] >> 8) : rlm@33: (gbPalette[index >> 1] & 0x00ff)); rlm@33: } rlm@33: return; rlm@33: } rlm@33: break; rlm@33: } rlm@33: rlm@33: // OCPS rlm@33: case 0x6a: rlm@33: { rlm@33: if (gbCgbMode) rlm@33: { rlm@33: int paletteIndex = (value & 0x3f) >> 1; rlm@33: int paletteHiLo = (value & 0x01); rlm@33: rlm@33: paletteIndex += 32; rlm@33: rlm@33: gbMemory[0xff6a] = value; rlm@33: gbMemory[0xff6b] = (paletteHiLo ? rlm@33: (gbPalette[paletteIndex] >> 8) : rlm@33: (gbPalette[paletteIndex] & 0x00ff)); rlm@33: return; rlm@33: } rlm@33: break; rlm@33: } rlm@33: rlm@33: // OCPD rlm@33: case 0x6b: rlm@33: { rlm@33: if (gbCgbMode) rlm@33: { rlm@33: int v = gbMemory[0xff6a]; rlm@33: int paletteIndex = (v & 0x3f) >> 1; rlm@33: int paletteHiLo = (v & 0x01); rlm@33: rlm@33: paletteIndex += 32; rlm@33: rlm@33: gbMemory[0xff6b] = value; rlm@33: gbPalette[paletteIndex] = (paletteHiLo ? rlm@33: ((value << 8) | (gbPalette[paletteIndex] & 0xff)) : rlm@33: ((gbPalette[paletteIndex] & 0xff00) | (value))) & 0x7fff; rlm@33: if (gbMemory[0xff6a] & 0x80) rlm@33: { rlm@33: int index = ((gbMemory[0xff6a] & 0x3f) + 1) & 0x3f; rlm@33: rlm@33: gbMemory[0xff6a] = (gbMemory[0xff6a] & 0x80) | index; rlm@33: rlm@33: gbMemory[0xff6b] = (index & 1 ? rlm@33: (gbPalette[(index >> 1) + 32] >> 8) : rlm@33: (gbPalette[(index >> 1) + 32] & 0x00ff)); rlm@33: } rlm@33: return; rlm@33: } rlm@33: break; rlm@33: } rlm@33: rlm@33: // SVBK rlm@33: case 0x70: rlm@33: { rlm@33: if (gbCgbMode) rlm@33: { rlm@33: value = value & 7; rlm@33: rlm@33: int bank = value; rlm@33: if (value == 0) rlm@33: bank = 1; rlm@33: rlm@33: if (bank == gbWramBank) rlm@33: return; rlm@33: rlm@33: int wramAddress = bank * 0x1000; rlm@33: gbMemoryMap[0x0d] = &gbWram[wramAddress]; rlm@33: rlm@33: gbWramBank = bank; rlm@33: register_SVBK = value; rlm@33: return; rlm@33: } rlm@33: break; rlm@33: } rlm@33: rlm@33: case 0xff: rlm@33: { rlm@33: register_IE = value; rlm@33: register_IF &= value; rlm@33: return; rlm@33: } rlm@33: } rlm@33: rlm@33: gbWriteMemoryQuick(address, value); rlm@33: } rlm@33: rlm@33: u8 gbReadOpcode(register u16 address) rlm@33: { rlm@33: if (gbCheatMap[address]) rlm@33: return gbCheatRead(address); rlm@33: rlm@33: // the following fix does more than Echo RAM fix, anyway... rlm@33: switch (gbEchoRAMFixOn ? (address >> 12) & 0x000f : address & 0xf000) rlm@33: { rlm@33: case 0x0a: rlm@33: case 0x0b: rlm@33: if (mapperReadRAM) rlm@33: return mapperReadRAM(address); rlm@33: break; rlm@33: case 0x0f: rlm@33: if (address > 0xff00) rlm@33: { rlm@33: switch (address & 0x00ff) rlm@33: { rlm@33: case 0x04: rlm@33: return register_DIV; rlm@33: case 0x05: rlm@33: return register_TIMA; rlm@33: case 0x06: rlm@33: return register_TMA; rlm@33: case 0x07: rlm@33: return (0xf8 | register_TAC); rlm@33: case 0x0f: rlm@33: return (0xe0 | register_IF); rlm@33: case 0x40: rlm@33: return register_LCDC; rlm@33: case 0x41: rlm@33: return (0x80 | register_STAT); rlm@33: case 0x42: rlm@33: return register_SCY; rlm@33: case 0x43: rlm@33: return register_SCX; rlm@33: case 0x44: rlm@33: return register_LY; rlm@33: case 0x45: rlm@33: return register_LYC; rlm@33: case 0x46: rlm@33: return register_DMA; rlm@33: case 0x4a: rlm@33: return register_WY; rlm@33: case 0x4b: rlm@33: return register_WX; rlm@33: case 0x4f: rlm@33: return (0xfe | register_VBK); rlm@33: case 0x51: rlm@33: return register_HDMA1; rlm@33: case 0x52: rlm@33: return register_HDMA2; rlm@33: case 0x53: rlm@33: return register_HDMA3; rlm@33: case 0x54: rlm@33: return register_HDMA4; rlm@33: case 0x55: rlm@33: return register_HDMA5; rlm@33: case 0x70: rlm@33: return (0xf8 | register_SVBK); rlm@33: case 0xff: rlm@33: return register_IE; rlm@33: } rlm@1: } rlm@33: break; rlm@33: } rlm@33: return gbReadMemoryQuick(address); rlm@33: } rlm@33: rlm@33: void gbWriteMemory(register u16 address, register u8 value) rlm@33: { rlm@33: gbWriteMemoryWrapped(address, value); rlm@33: CallRegisteredLuaMemHook(address, 1, value, LUAMEMHOOK_WRITE); rlm@33: } rlm@33: rlm@33: u8 gbReadMemory(register u16 address) rlm@33: { rlm@33: if (gbCheatMap[address]) rlm@33: return gbCheatRead(address); rlm@33: rlm@33: if (address < 0xa000) rlm@33: return gbReadMemoryQuick(address); rlm@33: rlm@33: if (address < 0xc000) rlm@33: { rlm@33: #ifndef FINAL_VERSION rlm@33: if (memorydebug) rlm@1: { rlm@33: log("Memory register read %04x PC=%04x\n", rlm@33: address, rlm@33: PC.W); rlm@1: } rlm@1: #endif rlm@1: rlm@33: if (mapperReadRAM) rlm@33: return mapperReadRAM(address); rlm@33: return gbReadMemoryQuick(address); rlm@33: } rlm@33: rlm@33: if (address >= 0xff00) rlm@33: { rlm@33: switch (address & 0x00ff) rlm@1: { rlm@1: case 0x00: rlm@33: { rlm@33: if (gbSgbMode) rlm@33: { rlm@33: gbSgbReadingController |= 4; rlm@33: gbSgbResetPacketState(); rlm@33: } rlm@33: rlm@33: int b = gbMemory[0xff00]; rlm@33: rlm@33: if ((b & 0x30) == 0x20) rlm@33: { rlm@33: b &= 0xf0; rlm@33: rlm@33: int joy = 0; rlm@33: if (gbSgbMode && gbSgbMultiplayer) rlm@33: { rlm@33: switch (gbSgbNextController) rlm@33: { rlm@33: case 0x0f: rlm@33: joy = 0; rlm@33: break; rlm@33: case 0x0e: rlm@33: joy = 1; rlm@33: break; rlm@33: case 0x0d: rlm@33: joy = 2; rlm@33: break; rlm@33: case 0x0c: rlm@33: joy = 3; rlm@33: break; rlm@33: default: rlm@33: joy = 0; rlm@33: } rlm@33: } rlm@33: int joystate = gbJoymask[joy]; rlm@33: if (!(joystate & 128)) rlm@33: b |= 0x08; rlm@33: if (!(joystate & 64)) rlm@33: b |= 0x04; rlm@33: if (!(joystate & 32)) rlm@33: b |= 0x02; rlm@33: if (!(joystate & 16)) rlm@33: b |= 0x01; rlm@33: rlm@33: gbMemory[0xff00] = b; rlm@33: } rlm@33: else if ((b & 0x30) == 0x10) rlm@33: { rlm@33: b &= 0xf0; rlm@33: rlm@33: int joy = 0; rlm@33: if (gbSgbMode && gbSgbMultiplayer) rlm@33: { rlm@33: switch (gbSgbNextController) rlm@33: { rlm@33: case 0x0f: rlm@33: joy = 0; rlm@33: break; rlm@33: case 0x0e: rlm@33: joy = 1; rlm@33: break; rlm@33: case 0x0d: rlm@33: joy = 2; rlm@33: break; rlm@33: case 0x0c: rlm@33: joy = 3; rlm@33: break; rlm@33: default: rlm@33: joy = 0; rlm@33: } rlm@33: } rlm@33: int joystate = gbJoymask[joy]; rlm@33: if (!(joystate & 8)) rlm@33: b |= 0x08; rlm@33: if (!(joystate & 4)) rlm@33: b |= 0x04; rlm@33: if (!(joystate & 2)) rlm@33: b |= 0x02; rlm@33: if (!(joystate & 1)) rlm@33: b |= 0x01; rlm@33: rlm@33: gbMemory[0xff00] = b; rlm@33: } rlm@33: else rlm@33: { rlm@33: if (gbSgbMode && gbSgbMultiplayer) rlm@33: { rlm@33: gbMemory[0xff00] = 0xf0 | gbSgbNextController; rlm@33: } rlm@33: else rlm@33: { rlm@33: gbMemory[0xff00] = 0xff; rlm@33: } rlm@33: } rlm@33: } rlm@33: GBSystemCounters.lagged = false; rlm@33: return gbMemory[0xff00]; rlm@33: break; rlm@33: case 0x01: rlm@33: return gbMemory[0xff01]; rlm@33: case 0x04: rlm@33: return register_DIV; rlm@33: case 0x05: rlm@33: return register_TIMA; rlm@33: case 0x06: rlm@33: return register_TMA; rlm@33: case 0x07: rlm@33: return (0xf8 | register_TAC); rlm@33: case 0x0f: rlm@33: return (0xe0 | register_IF); rlm@33: case 0x40: rlm@33: return register_LCDC; rlm@33: case 0x41: rlm@33: return (0x80 | register_STAT); rlm@33: case 0x42: rlm@33: return register_SCY; rlm@33: case 0x43: rlm@33: return register_SCX; rlm@33: case 0x44: rlm@33: return register_LY; rlm@33: case 0x45: rlm@33: return register_LYC; rlm@33: case 0x46: rlm@33: return register_DMA; rlm@33: case 0x4a: rlm@33: return register_WY; rlm@33: case 0x4b: rlm@33: return register_WX; rlm@33: case 0x4f: rlm@33: return (0xfe | register_VBK); rlm@33: case 0x51: rlm@33: return register_HDMA1; rlm@33: case 0x52: rlm@33: return register_HDMA2; rlm@33: case 0x53: rlm@33: return register_HDMA3; rlm@33: case 0x54: rlm@33: return register_HDMA4; rlm@33: case 0x55: rlm@33: return register_HDMA5; rlm@33: case 0x70: rlm@33: return (0xf8 | register_SVBK); rlm@33: case 0xff: rlm@33: return register_IE; rlm@1: } rlm@33: } rlm@33: rlm@33: return gbReadMemoryQuick(address); rlm@1: } rlm@1: rlm@1: void gbVblank_interrupt() rlm@1: { rlm@33: if (IFF & 0x80) rlm@33: { rlm@33: PC.W++; rlm@33: IFF &= 0x7f; rlm@33: } rlm@33: gbInterrupt &= 0xfe; rlm@33: rlm@33: IFF &= 0x7e; rlm@33: register_IF &= 0xfe; rlm@33: rlm@33: gbWriteMemory(--SP.W, PC.B.B1); rlm@33: gbWriteMemory(--SP.W, PC.B.B0); rlm@33: PC.W = 0x40; rlm@1: } rlm@1: rlm@1: void gbLcd_interrupt() rlm@1: { rlm@33: if (IFF & 0x80) rlm@33: { rlm@33: PC.W++; rlm@33: IFF &= 0x7f; rlm@33: } rlm@33: gbInterrupt &= 0xfd; rlm@33: IFF &= 0x7e; rlm@33: register_IF &= 0xfd; rlm@33: rlm@33: gbWriteMemory(--SP.W, PC.B.B1); rlm@33: gbWriteMemory(--SP.W, PC.B.B0); rlm@33: rlm@33: PC.W = 0x48; rlm@1: } rlm@1: rlm@1: void gbTimer_interrupt() rlm@1: { rlm@33: if (IFF & 0x80) rlm@33: { rlm@33: PC.W++; rlm@33: IFF &= 0x7f; rlm@33: } rlm@33: IFF &= 0x7e; rlm@33: gbInterrupt &= 0xfb; rlm@33: register_IF &= 0xfb; rlm@33: rlm@33: gbWriteMemory(--SP.W, PC.B.B1); rlm@33: gbWriteMemory(--SP.W, PC.B.B0); rlm@33: rlm@33: PC.W = 0x50; rlm@1: } rlm@1: rlm@1: void gbSerial_interrupt() rlm@1: { rlm@33: if (IFF & 0x80) rlm@33: { rlm@33: PC.W++; rlm@33: IFF &= 0x7f; rlm@33: } rlm@33: IFF &= 0x7e; rlm@33: gbInterrupt &= 0xf7; rlm@33: register_IF &= 0xf7; rlm@33: rlm@33: gbWriteMemory(--SP.W, PC.B.B1); rlm@33: gbWriteMemory(--SP.W, PC.B.B0); rlm@33: rlm@33: PC.W = 0x58; rlm@1: } rlm@1: rlm@1: void gbJoypad_interrupt() rlm@1: { rlm@33: if (IFF & 0x80) rlm@33: { rlm@33: PC.W++; rlm@33: IFF &= 0x7f; rlm@33: } rlm@33: IFF &= 0x7e; rlm@33: gbInterrupt &= 0xef; rlm@33: register_IF &= 0xef; rlm@33: rlm@33: gbWriteMemory(--SP.W, PC.B.B1); rlm@33: gbWriteMemory(--SP.W, PC.B.B0); rlm@33: rlm@33: PC.W = 0x60; rlm@1: } rlm@1: rlm@1: void gbSpeedSwitch() rlm@1: { rlm@33: if (gbSpeed == 0) rlm@33: { rlm@33: gbSpeed = 1; rlm@33: GBLCD_MODE_0_CLOCK_TICKS = 51 * 2; //127; //51 * 2; rlm@33: GBLCD_MODE_1_CLOCK_TICKS = 1140 * 2; rlm@33: GBLCD_MODE_2_CLOCK_TICKS = 20 * 2; //52; //20 * 2; rlm@33: GBLCD_MODE_3_CLOCK_TICKS = 43 * 2; //99; //43 * 2; rlm@33: GBDIV_CLOCK_TICKS = 64 * 2; rlm@33: GBLY_INCREMENT_CLOCK_TICKS = 114 * 2; rlm@33: GBTIMER_MODE_0_CLOCK_TICKS = 256; //256*2; rlm@33: GBTIMER_MODE_1_CLOCK_TICKS = 4; //4*2; rlm@33: GBTIMER_MODE_2_CLOCK_TICKS = 16; //16*2; rlm@33: GBTIMER_MODE_3_CLOCK_TICKS = 64; //64*2; rlm@33: GBSERIAL_CLOCK_TICKS = 128 * 2; rlm@33: gbDivTicks *= 2; rlm@33: gbLcdTicks *= 2; rlm@33: gbLcdLYIncrementTicks *= 2; rlm@33: // timerTicks *= 2; rlm@33: // timerClockTicks *= 2; rlm@33: gbSerialTicks *= 2; rlm@33: SOUND_CLOCK_TICKS = soundQuality * GB_USE_TICKS_AS * 2; rlm@33: soundTicks *= 2; rlm@33: // synchronizeTicks *= 2; rlm@33: // SYNCHRONIZE_CLOCK_TICKS *= 2; rlm@33: } rlm@33: else rlm@33: { rlm@33: gbSpeed = 0; rlm@33: GBLCD_MODE_0_CLOCK_TICKS = 51; rlm@33: GBLCD_MODE_1_CLOCK_TICKS = 1140; rlm@33: GBLCD_MODE_2_CLOCK_TICKS = 20; rlm@33: GBLCD_MODE_3_CLOCK_TICKS = 43; rlm@33: GBDIV_CLOCK_TICKS = 64; rlm@33: GBLY_INCREMENT_CLOCK_TICKS = 114; rlm@33: GBTIMER_MODE_0_CLOCK_TICKS = 256; rlm@33: GBTIMER_MODE_1_CLOCK_TICKS = 4; rlm@33: GBTIMER_MODE_2_CLOCK_TICKS = 16; rlm@33: GBTIMER_MODE_3_CLOCK_TICKS = 64; rlm@33: GBSERIAL_CLOCK_TICKS = 128; rlm@33: gbDivTicks /= 2; rlm@33: gbLcdTicks /= 2; rlm@33: gbLcdLYIncrementTicks /= 2; rlm@33: // timerTicks /= 2; rlm@33: // timerClockTicks /= 2; rlm@33: gbSerialTicks /= 2; rlm@33: SOUND_CLOCK_TICKS = soundQuality * GB_USE_TICKS_AS; rlm@33: soundTicks /= 2; rlm@33: // synchronizeTicks /= 2; rlm@33: // SYNCHRONIZE_CLOCK_TICKS /= 2; rlm@33: } rlm@1: } rlm@1: rlm@1: void gbGetHardwareType() rlm@1: { rlm@33: gbCgbMode = 0; rlm@33: if (gbRom[0x143] & 0x80) rlm@33: { rlm@33: if (gbEmulatorType == 0 || rlm@33: gbEmulatorType == 1 || rlm@33: gbEmulatorType == 4 || rlm@33: gbEmulatorType == 5 || rlm@33: (gbRom[0x146] != 0x03 && (gbEmulatorType == 2))) rlm@1: { rlm@33: gbCgbMode = 1; rlm@1: } rlm@33: } rlm@33: rlm@33: if (gbSgbMode == 2) rlm@33: { rlm@33: gbSgbMode = 0; rlm@33: return; rlm@33: } rlm@33: rlm@33: gbSgbMode = 0; rlm@33: if (gbRom[0x146] == 0x03) rlm@33: { rlm@33: if (gbEmulatorType == 0 || rlm@33: gbEmulatorType == 2 || rlm@33: gbEmulatorType == 5 || rlm@33: (!(gbRom[0x143] & 0x80) && (gbEmulatorType == 1 || gbEmulatorType == 4))) rlm@33: gbSgbMode = 1; rlm@33: } rlm@1: } rlm@1: rlm@1: void gbReset(bool userReset) rlm@1: { rlm@33: // movie must be closed while opening/creating a movie rlm@33: if (userReset && VBAMovieRecording()) rlm@33: { rlm@33: VBAMovieSignalReset(); rlm@33: return; rlm@33: } rlm@33: rlm@33: if (!VBAMovieActive()) rlm@33: { rlm@33: GBSystemCounters.frameCount = 0; rlm@33: GBSystemCounters.lagCount = 0; rlm@33: GBSystemCounters.extraCount = 0; rlm@33: GBSystemCounters.lagged = true; rlm@33: GBSystemCounters.laggedLast = true; rlm@33: } rlm@33: rlm@33: SP.W = 0xfffe; rlm@33: AF.W = 0x01b0; rlm@33: BC.W = 0x0013; rlm@33: DE.W = 0x00d8; rlm@33: HL.W = 0x014d; rlm@33: PC.W = 0x0100; rlm@33: IFF = 0; rlm@33: gbInterrupt = 1; rlm@33: gbInterruptWait = 0; rlm@33: rlm@33: register_DIV = 0; rlm@33: register_TIMA = 0; rlm@33: register_TMA = 0; rlm@33: register_TAC = 0; rlm@33: register_IF = 1; rlm@33: register_LCDC = 0x91; rlm@33: register_STAT = 0; rlm@33: register_SCY = 0; rlm@33: register_SCX = 0; rlm@33: register_LY = 0; rlm@33: register_LYC = 0; rlm@33: register_DMA = 0; rlm@33: register_WY = 0; rlm@33: register_WX = 0; rlm@33: register_VBK = 0; rlm@33: register_HDMA1 = 0; rlm@33: register_HDMA2 = 0; rlm@33: register_HDMA3 = 0; rlm@33: register_HDMA4 = 0; rlm@33: register_HDMA5 = 0; rlm@33: register_SVBK = 0; rlm@33: register_IE = 0; rlm@33: rlm@33: gbGetHardwareType(); rlm@33: if (gbCgbMode) rlm@33: { rlm@33: if (!gbVram) rlm@33: gbVram = (u8 *)malloc(0x4000 + 4); rlm@33: if (!gbWram) rlm@33: gbWram = (u8 *)malloc(0x8000 + 4); rlm@33: memset(gbVram, 0, 0x4000 + 4); rlm@33: memset(gbWram, 0, 0x8000 + 4); rlm@33: } rlm@33: else rlm@33: { rlm@33: if (gbVram) rlm@1: { rlm@33: free(gbVram); rlm@33: gbVram = NULL; rlm@1: } rlm@33: if (gbWram) rlm@1: { rlm@33: free(gbWram); rlm@33: gbWram = NULL; rlm@1: } rlm@33: } rlm@33: rlm@33: // clean LineBuffer rlm@33: if (gbLineBuffer) rlm@33: memset(gbLineBuffer, 0, 160 * sizeof(u16)); rlm@33: // clean Pix rlm@33: if (pix) rlm@33: memset(pix, 0, 4 * 257 * 226); rlm@33: rlm@33: if (gbCgbMode) rlm@33: { rlm@33: if (gbSgbMode) rlm@1: { rlm@33: if (gbEmulatorType == 5) rlm@33: AF.W = 0xffb0; rlm@33: else rlm@33: AF.W = 0x01b0; rlm@33: BC.W = 0x0013; rlm@33: DE.W = 0x00d8; rlm@33: HL.W = 0x014d; rlm@1: } rlm@33: else rlm@1: { rlm@33: AF.W = 0x11b0; rlm@33: BC.W = 0x0000; rlm@33: DE.W = 0xff56; rlm@33: HL.W = 0x000d; rlm@1: } rlm@33: if (gbEmulatorType == 4) rlm@33: BC.B.B1 |= 0x01; rlm@33: rlm@33: register_HDMA5 = 0xff; rlm@33: gbMemory[0xff68] = 0xc0; rlm@33: gbMemory[0xff6a] = 0xc0; rlm@33: rlm@33: for (int i = 0; i < 64; i++) rlm@33: gbPalette[i] = 0x7fff; rlm@33: } rlm@33: else rlm@33: { rlm@33: for (int i = 0; i < 8; i++) rlm@33: gbPalette[i] = systemGbPalette[gbPaletteOption * 8 + i]; rlm@33: } rlm@33: rlm@33: if (gbSpeed) rlm@33: { rlm@33: gbSpeedSwitch(); rlm@33: gbMemory[0xff4d] = 0; rlm@33: } rlm@33: rlm@33: gbDivTicks = GBDIV_CLOCK_TICKS; rlm@33: gbLcdMode = 2; rlm@33: gbLcdTicks = GBLCD_MODE_2_CLOCK_TICKS; rlm@33: gbLcdLYIncrementTicks = 0; rlm@33: gbTimerTicks = 0; rlm@33: gbTimerClockTicks = 0; rlm@33: gbSerialTicks = 0; rlm@33: gbSerialBits = 0; rlm@33: gbSerialOn = 0; rlm@33: gbWindowLine = -1; rlm@33: gbTimerOn = 0; rlm@33: gbTimerMode = 0; rlm@33: // gbSynchronizeTicks = GBSYNCHRONIZE_CLOCK_TICKS; rlm@33: gbSpeed = 0; rlm@33: gbJoymask[0] = gbJoymask[1] = gbJoymask[2] = gbJoymask[3] = 0; rlm@33: rlm@33: // FIXME: horrible kludge rlm@33: memset(s_gbJoymask, 0, sizeof(s_gbJoymask)); rlm@33: rlm@33: if (gbCgbMode) rlm@33: { rlm@33: gbSpeed = 0; rlm@33: gbHdmaOn = 0; rlm@33: gbHdmaSource = 0x0000; rlm@33: gbHdmaDestination = 0x8000; rlm@33: gbVramBank = 0; rlm@33: gbWramBank = 1; rlm@33: register_LY = 0x90; rlm@33: gbLcdMode = 1; rlm@33: } rlm@33: rlm@33: if (gbSgbMode) rlm@33: { rlm@33: gbSgbReset(); rlm@33: } rlm@33: rlm@33: for (int i = 0; i < 4; i++) rlm@33: gbBgp[i] = gbObp0[i] = gbObp1[i] = i; rlm@33: rlm@33: memset(&gbDataMBC1, 0, sizeof(gbDataMBC1)); rlm@33: gbDataMBC1.mapperROMBank = 1; rlm@33: rlm@33: gbDataMBC2.mapperRAMEnable = 0; rlm@33: gbDataMBC2.mapperROMBank = 1; rlm@33: rlm@33: memset(&gbDataMBC3, 0, 6 * sizeof(int32)); rlm@33: gbDataMBC3.mapperROMBank = 1; rlm@33: rlm@33: memset(&gbDataMBC5, 0, sizeof(gbDataMBC5)); rlm@33: gbDataMBC5.mapperROMBank = 1; rlm@33: switch (gbRom[0x147]) rlm@33: { rlm@33: case 0x1c: rlm@33: case 0x1d: rlm@33: case 0x1e: rlm@33: gbDataMBC5.isRumbleCartridge = 1; rlm@33: } rlm@33: rlm@33: memset(&gbDataHuC1, 0, sizeof(gbDataHuC1)); rlm@33: gbDataHuC1.mapperROMBank = 1; rlm@33: rlm@33: memset(&gbDataHuC3, 0, sizeof(gbDataHuC3)); rlm@33: gbDataHuC3.mapperROMBank = 1; rlm@33: rlm@33: gbMemoryMap[0x00] = &gbRom[0x0000]; rlm@33: gbMemoryMap[0x01] = &gbRom[0x1000]; rlm@33: gbMemoryMap[0x02] = &gbRom[0x2000]; rlm@33: gbMemoryMap[0x03] = &gbRom[0x3000]; rlm@33: gbMemoryMap[0x04] = &gbRom[0x4000]; rlm@33: gbMemoryMap[0x05] = &gbRom[0x5000]; rlm@33: gbMemoryMap[0x06] = &gbRom[0x6000]; rlm@33: gbMemoryMap[0x07] = &gbRom[0x7000]; rlm@33: if (gbCgbMode) rlm@33: { rlm@33: gbMemoryMap[0x08] = &gbVram[0x0000]; rlm@33: gbMemoryMap[0x09] = &gbVram[0x1000]; rlm@33: gbMemoryMap[0x0a] = &gbMemory[0xa000]; rlm@33: gbMemoryMap[0x0b] = &gbMemory[0xb000]; rlm@33: gbMemoryMap[0x0c] = &gbMemory[0xc000]; rlm@33: gbMemoryMap[0x0d] = &gbWram[0x1000]; rlm@33: gbMemoryMap[0x0e] = &gbMemory[0xe000]; rlm@33: gbMemoryMap[0x0f] = &gbMemory[0xf000]; rlm@33: } rlm@33: else rlm@33: { rlm@33: gbMemoryMap[0x08] = &gbMemory[0x8000]; rlm@33: gbMemoryMap[0x09] = &gbMemory[0x9000]; rlm@33: gbMemoryMap[0x0a] = &gbMemory[0xa000]; rlm@33: gbMemoryMap[0x0b] = &gbMemory[0xb000]; rlm@33: gbMemoryMap[0x0c] = &gbMemory[0xc000]; rlm@33: gbMemoryMap[0x0d] = &gbMemory[0xd000]; rlm@33: gbMemoryMap[0x0e] = &gbMemory[0xe000]; rlm@33: gbMemoryMap[0x0f] = &gbMemory[0xf000]; rlm@33: } rlm@33: rlm@33: if (gbRam) rlm@33: { rlm@33: gbMemoryMap[0x0a] = &gbRam[0x0000]; rlm@33: gbMemoryMap[0x0b] = &gbRam[0x1000]; rlm@33: } rlm@33: rlm@33: gbSoundReset(); rlm@33: rlm@33: systemResetSensor(); rlm@33: rlm@33: systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; rlm@33: rlm@33: gbLastTime = systemGetClock(); rlm@33: gbFrameCount = 0; rlm@33: rlm@33: systemRefreshScreen(); rlm@1: } rlm@1: rlm@1: void gbWriteSaveMBC1(const char *name) rlm@1: { rlm@33: FILE *gzFile = fopen(name, "wb"); rlm@33: rlm@33: if (gzFile == NULL) rlm@33: { rlm@33: systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); rlm@33: return; rlm@33: } rlm@33: rlm@33: fwrite(gbRam, rlm@33: 1, rlm@33: gbRamSize, rlm@33: gzFile); rlm@33: rlm@33: fclose(gzFile); rlm@1: } rlm@1: rlm@1: void gbWriteSaveMBC2(const char *name) rlm@1: { rlm@33: FILE *file = fopen(name, "wb"); rlm@33: rlm@33: if (file == NULL) rlm@33: { rlm@33: systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); rlm@33: return; rlm@33: } rlm@33: rlm@33: fwrite(&gbMemory[0xa000], rlm@33: 1, rlm@33: 256, rlm@33: file); rlm@33: rlm@33: fclose(file); rlm@1: } rlm@1: rlm@1: void gbWriteSaveMBC3(const char *name, bool extendedSave) rlm@1: { rlm@33: FILE *gzFile = fopen(name, "wb"); rlm@33: rlm@33: if (gzFile == NULL) rlm@33: { rlm@33: systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); rlm@33: return; rlm@33: } rlm@33: rlm@33: fwrite(gbRam, rlm@33: 1, rlm@33: gbRamSize, rlm@33: gzFile); rlm@33: rlm@33: if (extendedSave) rlm@33: { rlm@33: //assert(sizeof(time_t) == 4); rlm@33: fwrite(&gbDataMBC3.mapperSeconds, rlm@33: 1, rlm@33: 10 * sizeof(int32) + /*sizeof(time_t)*/4, rlm@33: gzFile); rlm@33: } rlm@33: rlm@33: fclose(gzFile); rlm@1: } rlm@1: rlm@1: void gbWriteSaveMBC5(const char *name) rlm@1: { rlm@33: FILE *gzFile = fopen(name, "wb"); rlm@33: rlm@33: if (gzFile == NULL) rlm@33: { rlm@33: systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); rlm@33: return; rlm@33: } rlm@33: rlm@33: fwrite(gbRam, rlm@33: 1, rlm@33: gbRamSize, rlm@33: gzFile); rlm@33: rlm@33: fclose(gzFile); rlm@1: } rlm@1: rlm@1: void gbWriteSaveMBC7(const char *name) rlm@1: { rlm@33: FILE *file = fopen(name, "wb"); rlm@33: rlm@33: if (file == NULL) rlm@33: { rlm@33: systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); rlm@33: return; rlm@33: } rlm@33: rlm@33: fwrite(&gbMemory[0xa000], rlm@33: 1, rlm@33: 256, rlm@33: file); rlm@33: rlm@33: fclose(file); rlm@1: } rlm@1: rlm@1: bool gbReadSaveMBC1(const char *name) rlm@1: { rlm@33: gzFile gzFile = gzopen(name, "rb"); rlm@33: rlm@33: if (gzFile == NULL) rlm@33: { rlm@33: return false; rlm@33: } rlm@33: rlm@33: int read = gzread(gzFile, rlm@33: gbRam, rlm@33: gbRamSize); rlm@33: rlm@33: if (read != gbRamSize) rlm@33: { rlm@33: systemMessage(MSG_FAILED_TO_READ_SGM, N_("Failed to read complete save game %s (%d)"), name, read); rlm@33: gzclose(gzFile); rlm@33: return false; rlm@33: } rlm@33: rlm@33: gzclose(gzFile); rlm@33: return true; rlm@1: } rlm@1: rlm@1: bool gbReadSaveMBC2(const char *name) rlm@1: { rlm@33: FILE *file = fopen(name, "rb"); rlm@33: rlm@33: if (file == NULL) rlm@33: { rlm@33: return false; rlm@33: } rlm@33: rlm@33: int read = fread(&gbMemory[0xa000], rlm@33: 1, rlm@33: 256, rlm@33: file); rlm@33: rlm@33: if (read != 256) rlm@33: { rlm@33: systemMessage(MSG_FAILED_TO_READ_SGM, rlm@33: N_("Failed to read complete save game %s (%d)"), name, read); rlm@33: fclose(file); rlm@33: return false; rlm@33: } rlm@33: rlm@33: fclose(file); rlm@33: return true; rlm@1: } rlm@1: rlm@1: bool gbReadSaveMBC3(const char *name) rlm@1: { rlm@33: gzFile gzFile = gzopen(name, "rb"); rlm@33: rlm@33: if (gzFile == NULL) rlm@33: { rlm@33: return false; rlm@33: } rlm@33: rlm@33: int read = gzread(gzFile, rlm@33: gbRam, rlm@33: gbRamSize); rlm@33: rlm@33: bool res = true; rlm@33: rlm@33: if (read != gbRamSize) rlm@33: { rlm@33: systemMessage(MSG_FAILED_TO_READ_SGM, rlm@33: N_("Failed to read complete save game %s (%d)"), name, read); rlm@33: } rlm@33: else rlm@33: { rlm@33: //assert(sizeof(time_t) == 4); rlm@33: read = gzread(gzFile, rlm@33: &gbDataMBC3.mapperSeconds, rlm@33: sizeof(int32) * 10 + /*sizeof(time_t)*/4); rlm@33: rlm@33: if (read != (sizeof(int32) * 10 + /*sizeof(time_t)*/4) && read != 0) rlm@1: { rlm@33: systemMessage(MSG_FAILED_TO_READ_RTC, rlm@33: N_("Failed to read RTC from save game %s (continuing)"), rlm@33: name); rlm@33: res = false; rlm@1: } rlm@33: } rlm@33: rlm@33: gzclose(gzFile); rlm@33: return res; rlm@1: } rlm@1: rlm@1: bool gbReadSaveMBC5(const char *name) rlm@1: { rlm@33: gzFile gzFile = gzopen(name, "rb"); rlm@33: rlm@33: if (gzFile == NULL) rlm@33: { rlm@33: return false; rlm@33: } rlm@33: rlm@33: int read = gzread(gzFile, rlm@33: gbRam, rlm@33: gbRamSize); rlm@33: rlm@33: if (read != gbRamSize) rlm@33: { rlm@33: systemMessage(MSG_FAILED_TO_READ_SGM, rlm@33: N_("Failed to read complete save game %s (%d)"), name, read); rlm@33: gzclose(gzFile); rlm@33: return false; rlm@33: } rlm@33: rlm@33: gzclose(gzFile); rlm@33: return true; rlm@1: } rlm@1: rlm@1: bool gbReadSaveMBC7(const char *name) rlm@1: { rlm@33: FILE *file = fopen(name, "rb"); rlm@33: rlm@33: if (file == NULL) rlm@33: { rlm@33: return false; rlm@33: } rlm@33: rlm@33: int read = fread(&gbMemory[0xa000], rlm@33: 1, rlm@33: 256, rlm@33: file); rlm@33: rlm@33: if (read != 256) rlm@33: { rlm@33: systemMessage(MSG_FAILED_TO_READ_SGM, rlm@33: N_("Failed to read complete save game %s (%d)"), name, read); rlm@33: fclose(file); rlm@33: return false; rlm@33: } rlm@33: rlm@33: fclose(file); rlm@33: return true; rlm@1: } rlm@1: rlm@1: #if 0 rlm@1: bool gbLoadBIOS(const char *biosFileName, bool useBiosFile) rlm@1: { rlm@33: useBios = false; rlm@33: if (useBiosFile) rlm@33: { rlm@33: useBios = utilLoadBIOS(bios, biosFileName, gbEmulatorType); rlm@33: if (!useBios) rlm@1: { rlm@33: systemMessage(MSG_INVALID_BIOS_FILE_SIZE, N_("Invalid BOOTROM file")); rlm@1: } rlm@33: } rlm@33: return useBios; rlm@1: } rlm@1: #endif rlm@1: rlm@1: void gbInit() rlm@1: { rlm@33: gbGenFilter(); rlm@33: gbSgbInit(); // calls gbSgbReset()... whatever rlm@33: rlm@33: gbMemory = (u8 *)malloc(65536 + 4); rlm@33: memset(gbMemory, 0, 65536 + 4); rlm@33: memset(gbPalette, 0, 2 * 128); rlm@33: rlm@33: // HACK: +4 at start to accomodate the 2xSaI filter reading out of bounds of the leftmost pixel rlm@33: origPix = (u8 *)calloc(1, 4 * 257 * 226 + 4); rlm@33: pix = origPix + 4; rlm@33: rlm@33: gbLineBuffer = (u16 *)malloc(160 * sizeof(u16)); rlm@1: } rlm@1: rlm@1: bool gbWriteBatteryFile(const char *file, bool extendedSave) rlm@1: { rlm@33: if (gbBattery) rlm@33: { rlm@33: int type = gbRom[0x147]; rlm@33: rlm@33: switch (type) rlm@1: { rlm@1: case 0x03: rlm@33: gbWriteSaveMBC1(file); rlm@33: break; rlm@33: case 0x06: rlm@33: gbWriteSaveMBC2(file); rlm@33: break; rlm@1: case 0x0f: rlm@1: case 0x10: rlm@1: case 0x13: rlm@33: gbWriteSaveMBC3(file, extendedSave); rlm@33: break; rlm@1: case 0x1b: rlm@1: case 0x1e: rlm@33: gbWriteSaveMBC5(file); rlm@33: break; rlm@33: case 0x22: rlm@33: gbWriteSaveMBC7(file); rlm@33: break; rlm@1: case 0xff: rlm@33: gbWriteSaveMBC1(file); rlm@33: break; rlm@33: } rlm@33: } rlm@33: return true; rlm@33: } rlm@33: rlm@33: bool gbWriteBatteryFile(const char *file) rlm@33: { rlm@33: gbWriteBatteryFile(file, true); rlm@33: return true; rlm@33: } rlm@33: rlm@33: bool gbWriteBatteryToStream(gzFile gzfile) rlm@33: { rlm@33: // the GB save code is ugly, so rather than convert it all to use gzFiles, just save it to a temp file... rlm@33: #define TEMP_SAVE_FNAME ("tempvbawrite.sav") rlm@33: bool retVal = gbWriteBatteryFile(TEMP_SAVE_FNAME, true); rlm@33: rlm@33: // ...open the temp file and figure out its size... rlm@33: FILE *fileTemp = fopen(TEMP_SAVE_FNAME, "rb"); rlm@33: if (fileTemp == NULL) rlm@33: return false; rlm@33: fseek(fileTemp, 0, SEEK_END); rlm@33: int len = (int) ftell(fileTemp); rlm@33: rlm@33: // ...copy over the temp file... rlm@33: char *temp = new char [len]; rlm@33: fseek(fileTemp, 0, SEEK_SET); rlm@33: if (fread(temp, len, 1, fileTemp) != 1) rlm@33: { rlm@33: delete [] temp; rlm@33: fclose(fileTemp); rlm@33: return false; rlm@33: } rlm@33: fclose(fileTemp); rlm@33: utilGzWrite(gzfile, temp, len); rlm@33: delete [] temp; rlm@33: rlm@33: // ... and delete the temp file rlm@33: remove(TEMP_SAVE_FNAME); rlm@33: #undef TEMP_SAVE_FNAME rlm@33: rlm@33: return retVal; rlm@33: } rlm@33: rlm@33: bool gbReadBatteryFile(const char *file) rlm@33: { rlm@33: bool res = false; rlm@33: if (gbBattery) rlm@33: { rlm@33: int type = gbRom[0x147]; rlm@33: rlm@33: switch (type) rlm@33: { rlm@33: case 0x03: rlm@33: res = gbReadSaveMBC1(file); rlm@33: break; rlm@1: case 0x06: rlm@33: res = gbReadSaveMBC2(file); rlm@33: break; rlm@33: case 0x0f: rlm@33: case 0x10: rlm@33: case 0x13: rlm@33: if (!gbReadSaveMBC3(file)) rlm@33: { rlm@33: struct tm *lt; rlm@33: time_t tmp; //Small kludge to get it working on some systems where time_t has size 8. rlm@33: rlm@33: if (VBAMovieActive() || VBAMovieLoading()) rlm@33: { rlm@33: gbDataMBC3.mapperLastTime = VBAMovieGetId() + VBAMovieGetFrameCounter() / 60; rlm@33: lt = gmtime(&tmp); rlm@33: gbDataMBC3.mapperLastTime=(u32)tmp; rlm@33: } rlm@33: else rlm@33: { rlm@33: time(&tmp); rlm@33: gbDataMBC3.mapperLastTime=(u32)tmp; rlm@33: lt = localtime(&tmp); rlm@33: } rlm@33: systemScreenMessage(ctime(&tmp), 4); rlm@33: gbDataMBC3.mapperLastTime=(u32)tmp; rlm@33: rlm@33: gbDataMBC3.mapperSeconds = lt->tm_sec; rlm@33: gbDataMBC3.mapperMinutes = lt->tm_min; rlm@33: gbDataMBC3.mapperHours = lt->tm_hour; rlm@33: gbDataMBC3.mapperDays = lt->tm_yday & 255; rlm@33: gbDataMBC3.mapperControl = (gbDataMBC3.mapperControl & 0xfe) | rlm@33: (lt->tm_yday > 255 ? 1 : 0); rlm@33: res = false; rlm@33: break; rlm@33: } rlm@33: time_t tmp; rlm@33: systemScreenMessage(ctime(&tmp), 4); rlm@33: gbDataMBC3.mapperLastTime=(u32)tmp; rlm@33: res = true; rlm@33: break; rlm@33: case 0x1b: rlm@33: case 0x1e: rlm@33: res = gbReadSaveMBC5(file); rlm@33: break; rlm@1: case 0x22: rlm@33: res = gbReadSaveMBC7(file); rlm@33: case 0xff: rlm@33: res = gbReadSaveMBC1(file); rlm@33: break; rlm@1: } rlm@33: } rlm@33: systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; rlm@33: return res; rlm@1: } rlm@1: rlm@33: bool gbReadBatteryFromStream(gzFile gzfile) rlm@33: { rlm@33: // the GB save code is ugly, so rather than convert it all to use gzFiles, just copy it to temp RAM... rlm@33: #define TEMP_SAVE_FNAME ("tempvbaread.sav") rlm@33: int pos = gztell(gzfile); rlm@33: int buflen = 1024; rlm@33: // ...make a temp file and write it there... rlm@33: FILE *fileTemp = fopen(TEMP_SAVE_FNAME, "wb"); rlm@33: if (fileTemp == NULL) rlm@33: return false; rlm@33: int gzDeflated; rlm@33: char *temp = new char [buflen]; rlm@33: while ((gzDeflated = utilGzRead(gzfile, temp, buflen)) != 0) rlm@33: { rlm@33: if (gzDeflated == -1 || fwrite(temp, gzDeflated, 1, fileTemp) != 1) rlm@33: { rlm@33: delete [] temp; rlm@33: fclose(fileTemp); rlm@33: gzseek(gzfile, pos, SEEK_SET); /// FIXME: leaves pos in gzfile before save instead of after it (everything that rlm@33: // calls this right now does a seek afterwards so it doesn't matter for now, but it's rlm@33: // still bad) rlm@33: return false; rlm@33: } rlm@33: } rlm@33: gzseek(gzfile, pos, SEEK_SET); /// FIXME: leaves pos in gzfile before save instead of after it (everything that calls this rlm@33: // right now does a seek afterwards so it doesn't matter for now, but it's still bad) rlm@33: fclose(fileTemp); rlm@33: delete [] temp; rlm@33: rlm@33: // ... load from the temp file... rlm@33: bool retVal = gbReadBatteryFile(TEMP_SAVE_FNAME); rlm@33: rlm@33: // ... and delete the temp file rlm@33: remove(TEMP_SAVE_FNAME); rlm@33: #undef TEMP_SAVE_FNAME rlm@33: rlm@33: return retVal; rlm@33: } rlm@33: rlm@33: bool gbReadGSASnapshot(const char *fileName) rlm@33: { rlm@33: FILE *file = fopen(fileName, "rb"); rlm@33: rlm@33: if (!file) rlm@33: { rlm@33: systemMessage(MSG_CANNOT_OPEN_FILE, N_("Cannot open file %s"), fileName); rlm@33: return false; rlm@33: } rlm@33: rlm@33: // long size = ftell(file); rlm@33: fseek(file, 0x4, SEEK_SET); rlm@33: char buffer[16]; rlm@33: char buffer2[16]; rlm@33: fread(buffer, 1, 15, file); rlm@33: buffer[15] = 0; rlm@33: memcpy(buffer2, &gbRom[0x134], 15); rlm@33: buffer2[15] = 0; rlm@33: if (memcmp(buffer, buffer2, 15)) rlm@33: { rlm@33: systemMessage(MSG_CANNOT_IMPORT_SNAPSHOT_FOR, rlm@33: N_("Cannot import snapshot for %s. Current game is %s"), rlm@33: buffer, rlm@33: buffer2); rlm@33: fclose(file); rlm@33: return false; rlm@33: } rlm@33: fseek(file, 0x13, SEEK_SET); rlm@33: int read = 0; rlm@33: int toRead = 0; rlm@33: switch (gbRom[0x147]) rlm@33: { rlm@33: case 0x03: rlm@33: case 0x0f: rlm@33: case 0x10: rlm@33: case 0x13: rlm@33: case 0x1b: rlm@33: case 0x1e: rlm@33: case 0xff: rlm@33: read = fread(gbRam, 1, gbRamSize, file); rlm@33: toRead = gbRamSize; rlm@33: break; rlm@33: case 0x06: rlm@33: case 0x22: rlm@33: read = fread(&gbMemory[0xa000], 1, 256, file); rlm@33: toRead = 256; rlm@33: break; rlm@33: default: rlm@33: systemMessage(MSG_UNSUPPORTED_SNAPSHOT_FILE, rlm@33: N_("Unsupported snapshot file %s"), rlm@33: fileName); rlm@33: fclose(file); rlm@33: return false; rlm@33: } rlm@33: fclose(file); rlm@33: gbReset(); rlm@33: return true; rlm@33: } rlm@33: rlm@1: variable_desc gbSaveGameStruct[] = rlm@33: { rlm@33: { &PC.W, sizeof(u16) }, rlm@33: { &SP.W, sizeof(u16) }, rlm@33: { &AF.W, sizeof(u16) }, rlm@33: { &BC.W, sizeof(u16) }, rlm@33: { &DE.W, sizeof(u16) }, rlm@33: { &HL.W, sizeof(u16) }, rlm@33: { &IFF, sizeof(u8) }, rlm@33: { &GBLCD_MODE_0_CLOCK_TICKS, sizeof(int32) }, rlm@33: { &GBLCD_MODE_1_CLOCK_TICKS, sizeof(int32) }, rlm@33: { &GBLCD_MODE_2_CLOCK_TICKS, sizeof(int32) }, rlm@33: { &GBLCD_MODE_3_CLOCK_TICKS, sizeof(int32) }, rlm@33: { &GBDIV_CLOCK_TICKS, sizeof(int32) }, rlm@33: { &GBLY_INCREMENT_CLOCK_TICKS, sizeof(int32) }, rlm@33: { &GBTIMER_MODE_0_CLOCK_TICKS, sizeof(int32) }, rlm@33: { &GBTIMER_MODE_1_CLOCK_TICKS, sizeof(int32) }, rlm@33: { &GBTIMER_MODE_2_CLOCK_TICKS, sizeof(int32) }, rlm@33: { &GBTIMER_MODE_3_CLOCK_TICKS, sizeof(int32) }, rlm@33: { &GBSERIAL_CLOCK_TICKS, sizeof(int32) }, rlm@33: { &GBSYNCHRONIZE_CLOCK_TICKS, sizeof(int32) }, rlm@33: { &gbDivTicks, sizeof(int32) }, rlm@33: { &gbLcdMode, sizeof(int32) }, rlm@33: { &gbLcdTicks, sizeof(int32) }, rlm@33: { &gbLcdLYIncrementTicks, sizeof(int32) }, rlm@33: { &gbTimerTicks, sizeof(int32) }, rlm@33: { &gbTimerClockTicks, sizeof(int32) }, rlm@33: { &gbSerialTicks, sizeof(int32) }, rlm@33: { &gbSerialBits, sizeof(int32) }, rlm@33: { &gbInterrupt, sizeof(int32) }, rlm@33: { &gbInterruptWait, sizeof(int32) }, rlm@33: { &gbSynchronizeTicks, sizeof(int32) }, rlm@33: { &gbTimerOn, sizeof(int32) }, rlm@33: { &gbTimerMode, sizeof(int32) }, rlm@33: { &gbSerialOn, sizeof(int32) }, rlm@33: { &gbWindowLine, sizeof(int32) }, rlm@33: { &gbCgbMode, sizeof(int32) }, rlm@33: { &gbVramBank, sizeof(int32) }, rlm@33: { &gbWramBank, sizeof(int32) }, rlm@33: { &gbHdmaSource, sizeof(int32) }, rlm@33: { &gbHdmaDestination, sizeof(int32) }, rlm@33: { &gbHdmaBytes, sizeof(int32) }, rlm@33: { &gbHdmaOn, sizeof(int32) }, rlm@33: { &gbSpeed, sizeof(int32) }, rlm@33: { &gbSgbMode, sizeof(int32) }, rlm@33: { ®ister_DIV, sizeof(u8) }, rlm@33: { ®ister_TIMA, sizeof(u8) }, rlm@33: { ®ister_TMA, sizeof(u8) }, rlm@33: { ®ister_TAC, sizeof(u8) }, rlm@33: { ®ister_IF, sizeof(u8) }, rlm@33: { ®ister_LCDC, sizeof(u8) }, rlm@33: { ®ister_STAT, sizeof(u8) }, rlm@33: { ®ister_SCY, sizeof(u8) }, rlm@33: { ®ister_SCX, sizeof(u8) }, rlm@33: { ®ister_LY, sizeof(u8) }, rlm@33: { ®ister_LYC, sizeof(u8) }, rlm@33: { ®ister_DMA, sizeof(u8) }, rlm@33: { ®ister_WY, sizeof(u8) }, rlm@33: { ®ister_WX, sizeof(u8) }, rlm@33: { ®ister_VBK, sizeof(u8) }, rlm@33: { ®ister_HDMA1, sizeof(u8) }, rlm@33: { ®ister_HDMA2, sizeof(u8) }, rlm@33: { ®ister_HDMA3, sizeof(u8) }, rlm@33: { ®ister_HDMA4, sizeof(u8) }, rlm@33: { ®ister_HDMA5, sizeof(u8) }, rlm@33: { ®ister_SVBK, sizeof(u8) }, rlm@33: { ®ister_IE, sizeof(u8) }, rlm@33: { &gbBgp[0], sizeof(u8) }, rlm@33: { &gbBgp[1], sizeof(u8) }, rlm@33: { &gbBgp[2], sizeof(u8) }, rlm@33: { &gbBgp[3], sizeof(u8) }, rlm@33: { &gbObp0[0], sizeof(u8) }, rlm@33: { &gbObp0[1], sizeof(u8) }, rlm@33: { &gbObp0[2], sizeof(u8) }, rlm@33: { &gbObp0[3], sizeof(u8) }, rlm@33: { &gbObp1[0], sizeof(u8) }, rlm@33: { &gbObp1[1], sizeof(u8) }, rlm@33: { &gbObp1[2], sizeof(u8) }, rlm@33: { &gbObp1[3], sizeof(u8) }, rlm@33: { NULL, 0 } rlm@33: }; rlm@1: rlm@1: bool gbWriteSaveStateToStream(gzFile gzFile) rlm@1: { rlm@33: utilWriteInt(gzFile, GBSAVE_GAME_VERSION); rlm@33: rlm@33: utilGzWrite(gzFile, &gbRom[0x134], 15); rlm@33: rlm@33: utilWriteData(gzFile, gbSaveGameStruct); rlm@33: rlm@33: utilGzWrite(gzFile, &IFF, 2); rlm@33: rlm@33: if (gbSgbMode) rlm@33: { rlm@33: gbSgbSaveGame(gzFile); rlm@33: } rlm@33: rlm@33: utilGzWrite(gzFile, &gbDataMBC1, sizeof(gbDataMBC1)); rlm@33: utilGzWrite(gzFile, &gbDataMBC2, sizeof(gbDataMBC2)); rlm@33: //assert(sizeof(time_t) == 4); rlm@33: utilGzWrite(gzFile, &gbDataMBC3, sizeof(gbDataMBC3)); rlm@33: utilGzWrite(gzFile, &gbDataMBC5, sizeof(gbDataMBC5)); rlm@33: utilGzWrite(gzFile, &gbDataHuC1, sizeof(gbDataHuC1)); rlm@33: utilGzWrite(gzFile, &gbDataHuC3, sizeof(gbDataHuC3)); rlm@33: rlm@33: // yes, this definitely needs to be saved, or loading paused games will show a black screen rlm@33: // this is also necessary to be consistent with what the GBA saving does rlm@33: utilGzWrite(gzFile, pix, 4 * 257 * 226); rlm@33: rlm@33: utilGzWrite(gzFile, gbPalette, 128 * sizeof(u16)); rlm@33: // todo: remove rlm@33: utilGzWrite(gzFile, gbPalette, 128 * sizeof(u16)); rlm@33: rlm@33: utilGzWrite(gzFile, &gbMemory[0x8000], 0x8000); rlm@33: rlm@33: if (gbRamSize && gbRam) rlm@33: { rlm@33: utilGzWrite(gzFile, gbRam, gbRamSize); rlm@33: } rlm@33: rlm@33: if (gbCgbMode) rlm@33: { rlm@33: utilGzWrite(gzFile, gbVram, 0x4000); rlm@33: utilGzWrite(gzFile, gbWram, 0x8000); rlm@33: } rlm@33: rlm@33: gbSoundSaveGame(gzFile); rlm@33: rlm@33: gbCheatsSaveGame(gzFile); rlm@33: rlm@33: // new to re-recording version: rlm@33: { rlm@33: extern int32 sensorX, sensorY; rlm@33: utilGzWrite(gzFile, &sensorX, sizeof(sensorX)); rlm@33: utilGzWrite(gzFile, &sensorY, sizeof(sensorY)); rlm@33: utilGzWrite(gzFile, gbJoymask, 4 * sizeof(*gbJoymask)); // this has to be saved or old input will incorrectly get rlm@33: // carried rlm@33: // back on loading a snapshot! rlm@33: rlm@33: bool8 movieActive = VBAMovieActive(); rlm@33: utilGzWrite(gzFile, &movieActive, sizeof(movieActive)); rlm@33: if (movieActive) rlm@33: { rlm@33: uint8 *movie_freeze_buf = NULL; rlm@33: uint32 movie_freeze_size = 0; rlm@33: rlm@33: VBAMovieFreeze(&movie_freeze_buf, &movie_freeze_size); rlm@33: if (movie_freeze_buf) rlm@33: { rlm@33: utilGzWrite(gzFile, &movie_freeze_size, sizeof(movie_freeze_size)); rlm@33: utilGzWrite(gzFile, movie_freeze_buf, movie_freeze_size); rlm@33: delete [] movie_freeze_buf; rlm@33: } rlm@33: else rlm@33: { rlm@33: systemMessage(0, N_("Failed to save movie snapshot.")); rlm@33: return false; rlm@33: } rlm@33: } rlm@33: utilGzWrite(gzFile, &GBSystemCounters.frameCount, sizeof(GBSystemCounters.frameCount)); rlm@33: } rlm@33: rlm@33: // new to rerecording 19.4 wip (svn r22+): rlm@33: { rlm@33: utilGzWrite(gzFile, &GBSystemCounters.lagCount, sizeof(GBSystemCounters.lagCount)); rlm@33: utilGzWrite(gzFile, &GBSystemCounters.lagged, sizeof(GBSystemCounters.lagged)); rlm@33: utilGzWrite(gzFile, &GBSystemCounters.laggedLast, sizeof(GBSystemCounters.laggedLast)); rlm@33: } rlm@33: rlm@33: return true; rlm@1: } rlm@1: rlm@1: bool gbWriteMemSaveState(char *memory, int available) rlm@1: { rlm@33: gzFile gzFile = utilMemGzOpen(memory, available, "w"); rlm@33: rlm@33: if (gzFile == NULL) rlm@33: { rlm@33: return false; rlm@33: } rlm@33: rlm@33: bool res = gbWriteSaveStateToStream(gzFile); rlm@33: rlm@33: long pos = utilGzTell(gzFile) + 8; rlm@33: rlm@33: if (pos >= (available)) rlm@33: res = false; rlm@33: rlm@33: utilGzClose(gzFile); rlm@33: rlm@33: return res; rlm@1: } rlm@1: rlm@1: bool gbWriteSaveState(const char *name) rlm@1: { rlm@33: gzFile gzFile = utilGzOpen(name, "wb"); rlm@33: rlm@33: if (gzFile == NULL) rlm@33: return false; rlm@33: rlm@33: bool res = gbWriteSaveStateToStream(gzFile); rlm@33: rlm@33: utilGzClose(gzFile); rlm@33: return res; rlm@1: } rlm@1: rlm@1: static int tempStateID = 0; rlm@1: static int tempFailCount = 0; rlm@1: static bool backupSafe = true; rlm@1: rlm@1: bool gbReadSaveStateFromStream(gzFile gzFile) rlm@1: { rlm@33: int type; rlm@33: char tempBackupName [128]; rlm@33: if (backupSafe) rlm@33: { rlm@33: sprintf(tempBackupName, "gbatempsave%d.sav", tempStateID++); rlm@33: gbWriteSaveState(tempBackupName); rlm@33: } rlm@33: rlm@33: int version = utilReadInt(gzFile); rlm@33: rlm@33: if (version > GBSAVE_GAME_VERSION || version < 0) rlm@33: { rlm@33: systemMessage(MSG_UNSUPPORTED_VB_SGM, rlm@33: N_("Unsupported VisualBoy save game version %d"), version); rlm@33: goto failedLoadGB; rlm@33: } rlm@33: rlm@33: u8 romname[20]; rlm@33: rlm@33: utilGzRead(gzFile, romname, 15); rlm@33: rlm@33: if (memcmp(&gbRom[0x134], romname, 15) != 0) rlm@33: { rlm@33: systemMessage(MSG_CANNOT_LOAD_SGM_FOR, rlm@33: N_("Cannot load save game for %s. Playing %s"), rlm@33: romname, &gbRom[0x134]); rlm@33: goto failedLoadGB; rlm@33: } rlm@33: rlm@33: utilReadData(gzFile, gbSaveGameStruct); rlm@33: rlm@33: if (version >= GBSAVE_GAME_VERSION_7) rlm@33: { rlm@33: utilGzRead(gzFile, &IFF, 2); rlm@33: } rlm@33: rlm@33: if (gbSgbMode) rlm@33: { rlm@33: gbSgbReadGame(gzFile, version); rlm@33: } rlm@33: else rlm@33: { rlm@33: gbSgbMask = 0; // loading a game at the wrong time causes no display rlm@33: } rlm@33: rlm@33: utilGzRead(gzFile, &gbDataMBC1, sizeof(gbDataMBC1)); rlm@33: utilGzRead(gzFile, &gbDataMBC2, sizeof(gbDataMBC2)); rlm@33: if (version < GBSAVE_GAME_VERSION_4) rlm@33: // prior to version 4, there was no adjustment for the time the game rlm@33: // was last played, so we have less to read. This needs update if the rlm@33: // structure changes again. rlm@33: utilGzRead(gzFile, &gbDataMBC3, sizeof(int32) * 10); rlm@33: else rlm@33: { rlm@33: //assert(sizeof(time_t) == 4); rlm@33: utilGzRead(gzFile, &gbDataMBC3, sizeof(gbDataMBC3)); rlm@33: } rlm@33: utilGzRead(gzFile, &gbDataMBC5, sizeof(gbDataMBC5)); rlm@33: utilGzRead(gzFile, &gbDataHuC1, sizeof(gbDataHuC1)); rlm@33: utilGzRead(gzFile, &gbDataHuC3, sizeof(gbDataHuC3)); rlm@33: rlm@33: if (version >= GBSAVE_GAME_VERSION_12) rlm@33: { rlm@33: utilGzRead(gzFile, pix, 4 * 257 * 226); rlm@33: } rlm@33: else rlm@33: { rlm@33: memset(pix, 0, 257 * 226 * sizeof(u32)); rlm@33: // if(version < GBSAVE_GAME_VERSION_5) rlm@33: // utilGzRead(gzFile, pix, 256*224*sizeof(u16)); rlm@33: } rlm@33: rlm@33: if (version < GBSAVE_GAME_VERSION_6) rlm@33: { rlm@33: utilGzRead(gzFile, gbPalette, 64 * sizeof(u16)); rlm@33: } rlm@33: else rlm@33: utilGzRead(gzFile, gbPalette, 128 * sizeof(u16)); rlm@33: rlm@33: // todo: remove rlm@33: utilGzRead(gzFile, gbPalette, 128 * sizeof(u16)); rlm@33: rlm@33: if (version < GBSAVE_GAME_VERSION_10) rlm@33: { rlm@33: if (!gbCgbMode && !gbSgbMode) rlm@1: { rlm@33: for (int i = 0; i < 8; i++) rlm@33: gbPalette[i] = systemGbPalette[gbPaletteOption * 8 + i]; rlm@1: } rlm@33: } rlm@33: rlm@33: utilGzRead(gzFile, &gbMemory[0x8000], 0x8000); rlm@33: rlm@33: if (gbRamSize && gbRam) rlm@33: { rlm@33: utilGzRead(gzFile, gbRam, gbRamSize); rlm@33: } rlm@33: rlm@33: gbMemoryMap[0x00] = &gbRom[0x0000]; rlm@33: gbMemoryMap[0x01] = &gbRom[0x1000]; rlm@33: gbMemoryMap[0x02] = &gbRom[0x2000]; rlm@33: gbMemoryMap[0x03] = &gbRom[0x3000]; rlm@33: gbMemoryMap[0x04] = &gbRom[0x4000]; rlm@33: gbMemoryMap[0x05] = &gbRom[0x5000]; rlm@33: gbMemoryMap[0x06] = &gbRom[0x6000]; rlm@33: gbMemoryMap[0x07] = &gbRom[0x7000]; rlm@33: gbMemoryMap[0x08] = &gbMemory[0x8000]; rlm@33: gbMemoryMap[0x09] = &gbMemory[0x9000]; rlm@33: gbMemoryMap[0x0a] = &gbMemory[0xa000]; rlm@33: gbMemoryMap[0x0b] = &gbMemory[0xb000]; rlm@33: gbMemoryMap[0x0c] = &gbMemory[0xc000]; rlm@33: gbMemoryMap[0x0d] = &gbMemory[0xd000]; rlm@33: gbMemoryMap[0x0e] = &gbMemory[0xe000]; rlm@33: gbMemoryMap[0x0f] = &gbMemory[0xf000]; rlm@33: rlm@33: type = gbRom[0x147]; rlm@33: rlm@33: switch (type) rlm@33: { rlm@33: case 0x00: rlm@33: case 0x01: rlm@33: case 0x02: rlm@33: case 0x03: rlm@33: // MBC 1 rlm@33: memoryUpdateMapMBC1(); rlm@33: break; rlm@33: case 0x05: rlm@33: case 0x06: rlm@33: // MBC2 rlm@33: memoryUpdateMapMBC2(); rlm@33: break; rlm@33: case 0x0f: rlm@33: case 0x10: rlm@33: case 0x11: rlm@33: case 0x12: rlm@33: case 0x13: rlm@33: // MBC 3 rlm@33: memoryUpdateMapMBC3(); rlm@33: break; rlm@33: case 0x19: rlm@33: case 0x1a: rlm@33: case 0x1b: rlm@33: // MBC5 rlm@33: memoryUpdateMapMBC5(); rlm@33: break; rlm@33: case 0x1c: rlm@33: case 0x1d: rlm@33: case 0x1e: rlm@33: // MBC 5 Rumble rlm@33: memoryUpdateMapMBC5(); rlm@33: break; rlm@33: case 0x22: rlm@33: // MBC 7 rlm@33: memoryUpdateMapMBC7(); rlm@33: break; rlm@33: case 0xfe: rlm@33: // HuC3 rlm@33: memoryUpdateMapHuC3(); rlm@33: break; rlm@33: case 0xff: rlm@33: // HuC1 rlm@33: memoryUpdateMapHuC1(); rlm@33: break; rlm@33: } rlm@33: rlm@33: if (gbCgbMode) rlm@33: { rlm@33: if (!gbVram) rlm@33: gbVram = (u8 *)malloc(0x4000 + 4); rlm@33: if (!gbWram) rlm@33: gbWram = (u8 *)malloc(0x8000 + 4); rlm@33: utilGzRead(gzFile, gbVram, 0x4000); rlm@33: utilGzRead(gzFile, gbWram, 0x8000); rlm@33: rlm@33: int value = register_SVBK; rlm@33: if (value == 0) rlm@33: value = 1; rlm@33: rlm@33: gbMemoryMap[0x08] = &gbVram[register_VBK * 0x2000]; rlm@33: gbMemoryMap[0x09] = &gbVram[register_VBK * 0x2000 + 0x1000]; rlm@33: gbMemoryMap[0x0d] = &gbWram[value * 0x1000]; rlm@33: } rlm@33: else rlm@33: { rlm@33: if (gbVram) rlm@1: { rlm@33: free(gbVram); rlm@33: gbVram = NULL; rlm@1: } rlm@33: if (gbWram) rlm@1: { rlm@33: free(gbWram); rlm@33: gbWram = NULL; rlm@1: } rlm@33: } rlm@33: rlm@33: gbSoundReadGame(version, gzFile); rlm@33: rlm@33: #if 0 rlm@33: if (gbBorderOn) rlm@33: { rlm@33: gbSgbRenderBorder(); rlm@33: } rlm@33: rlm@33: systemRefreshScreen(); rlm@33: #endif rlm@33: rlm@33: if (version > GBSAVE_GAME_VERSION_1) rlm@33: gbCheatsReadGame(gzFile, version); rlm@33: rlm@33: systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; rlm@33: rlm@33: if (version >= GBSAVE_GAME_VERSION_11) // new to re-recording version: rlm@33: { rlm@33: extern int32 sensorX, sensorY; // from SDL.cpp rlm@33: utilGzRead(gzFile, &sensorX, sizeof(sensorX)); rlm@33: utilGzRead(gzFile, &sensorY, sizeof(sensorY)); rlm@33: utilGzRead(gzFile, gbJoymask, 4 * sizeof(*gbJoymask)); // this has to be saved or old input will incorrectly get carried rlm@33: // back on loading a snapshot! rlm@33: rlm@33: bool8 movieSnapshot; rlm@33: utilGzRead(gzFile, &movieSnapshot, sizeof(movieSnapshot)); rlm@33: if (VBAMovieActive() && !movieSnapshot) rlm@1: { rlm@33: systemMessage(0, N_("Can't load a non-movie snapshot while a movie is active.")); rlm@33: goto failedLoadGB; rlm@1: } rlm@1: rlm@33: if (movieSnapshot) // even if a movie isn't active we still want to parse through this in case other stuff is added rlm@33: // later on in the save format rlm@1: { rlm@33: uint32 movieInputDataSize = 0; rlm@33: utilGzRead(gzFile, &movieInputDataSize, sizeof(movieInputDataSize)); rlm@33: uint8 *local_movie_data = new uint8 [movieInputDataSize]; rlm@33: int readBytes = utilGzRead(gzFile, local_movie_data, movieInputDataSize); rlm@33: if (readBytes != movieInputDataSize) rlm@33: { rlm@33: systemMessage(0, N_("Corrupt movie snapshot.")); rlm@33: if (local_movie_data) rlm@33: delete [] local_movie_data; rlm@33: goto failedLoadGB; rlm@33: } rlm@33: int code = VBAMovieUnfreeze(local_movie_data, movieInputDataSize); rlm@33: if (local_movie_data) rlm@33: delete [] local_movie_data; rlm@33: if (code != MOVIE_SUCCESS && VBAMovieActive()) rlm@33: { rlm@33: char errStr [1024]; rlm@33: strcpy(errStr, "Failed to load movie snapshot"); rlm@33: switch (code) rlm@33: { rlm@33: case MOVIE_NOT_FROM_THIS_MOVIE: rlm@33: strcat(errStr, ";\nSnapshot not from this movie"); break; rlm@33: case MOVIE_NOT_FROM_A_MOVIE: rlm@33: strcat(errStr, ";\nNot a movie snapshot"); break; // shouldn't get here... rlm@33: case MOVIE_SNAPSHOT_INCONSISTENT: rlm@33: strcat(errStr, ";\nSnapshot inconsistent with movie"); break; rlm@33: case MOVIE_WRONG_FORMAT: rlm@33: strcat(errStr, ";\nWrong format"); break; rlm@33: } rlm@33: strcat(errStr, "."); rlm@33: systemMessage(0, N_(errStr)); rlm@33: goto failedLoadGB; rlm@33: } rlm@1: } rlm@33: utilGzRead(gzFile, &GBSystemCounters.frameCount, sizeof(GBSystemCounters.frameCount)); rlm@33: } rlm@33: rlm@33: if (version >= GBSAVE_GAME_VERSION_13) // new to rerecording 19.4 wip (svn r22+): rlm@33: { rlm@33: utilGzRead(gzFile, &GBSystemCounters.lagCount, sizeof(GBSystemCounters.lagCount)); rlm@33: utilGzRead(gzFile, &GBSystemCounters.lagged, sizeof(GBSystemCounters.lagged)); rlm@33: utilGzRead(gzFile, &GBSystemCounters.laggedLast, sizeof(GBSystemCounters.laggedLast)); rlm@33: } rlm@33: rlm@33: if (backupSafe) rlm@33: { rlm@33: remove(tempBackupName); rlm@33: tempFailCount = 0; rlm@33: } rlm@33: rlm@33: for (int i = 0; i < 4; ++i) rlm@33: systemSetJoypad(i, gbJoymask[i] & 0xFFFF); rlm@33: rlm@33: // FIXME: horrible kludge rlm@33: memcpy(s_gbJoymask, gbJoymask, sizeof(gbJoymask)); rlm@33: rlm@33: VBAUpdateButtonPressDisplay(); rlm@33: VBAUpdateFrameCountDisplay(); rlm@33: systemRefreshScreen(); rlm@33: return true; rlm@33: rlm@33: failedLoadGB: rlm@33: if (backupSafe) rlm@33: { rlm@33: tempFailCount++; rlm@33: if (tempFailCount < 3) // fail no more than 2 times in a row rlm@33: gbReadSaveState(tempBackupName); rlm@33: remove(tempBackupName); rlm@33: } rlm@33: return false; rlm@1: } rlm@1: rlm@1: bool gbReadMemSaveState(char *memory, int available) rlm@1: { rlm@33: gzFile gzFile = utilMemGzOpen(memory, available, "r"); rlm@33: rlm@33: backupSafe = false; rlm@33: bool res = gbReadSaveStateFromStream(gzFile); rlm@33: backupSafe = true; rlm@33: rlm@33: utilGzClose(gzFile); rlm@33: rlm@33: return res; rlm@1: } rlm@1: rlm@1: bool gbReadSaveState(const char *name) rlm@1: { rlm@33: gzFile gzFile = utilGzOpen(name, "rb"); rlm@33: rlm@33: if (gzFile == NULL) rlm@33: { rlm@33: return false; rlm@33: } rlm@33: rlm@33: bool res = gbReadSaveStateFromStream(gzFile); rlm@33: rlm@33: utilGzClose(gzFile); rlm@33: rlm@33: return res; rlm@1: } rlm@1: rlm@1: bool gbWritePNGFile(const char *fileName) rlm@1: { rlm@33: if (gbBorderOn) rlm@33: return utilWritePNGFile(fileName, 256, 224, pix); rlm@33: return utilWritePNGFile(fileName, 160, 144, pix); rlm@1: } rlm@1: rlm@1: bool gbWriteBMPFile(const char *fileName) rlm@1: { rlm@33: if (gbBorderOn) rlm@33: return utilWriteBMPFile(fileName, 256, 224, pix); rlm@33: return utilWriteBMPFile(fileName, 160, 144, pix); rlm@1: } rlm@1: rlm@1: void gbCleanUp() rlm@1: { rlm@33: newFrame = true; rlm@33: rlm@33: GBSystemCounters.frameCount = 0; rlm@33: GBSystemCounters.lagCount = 0; rlm@33: GBSystemCounters.extraCount = 0; rlm@33: GBSystemCounters.lagged = true; rlm@33: GBSystemCounters.laggedLast = true; rlm@33: rlm@33: if (gbRam != NULL) rlm@33: { rlm@33: free(gbRam); rlm@33: gbRam = NULL; rlm@33: } rlm@33: rlm@33: if (gbRom != NULL) rlm@33: { rlm@33: free(gbRom); rlm@33: gbRom = NULL; rlm@33: } rlm@33: rlm@33: if (gbMemory != NULL) rlm@33: { rlm@33: free(gbMemory); rlm@33: gbMemory = NULL; rlm@33: } rlm@33: rlm@33: if (gbLineBuffer != NULL) rlm@33: { rlm@33: free(gbLineBuffer); rlm@33: gbLineBuffer = NULL; rlm@33: } rlm@33: rlm@33: if (origPix != NULL) rlm@33: { rlm@33: free(origPix); rlm@33: origPix = NULL; rlm@33: } rlm@33: pix = NULL; rlm@33: rlm@33: gbSgbShutdown(); rlm@33: rlm@33: if (gbVram != NULL) rlm@33: { rlm@33: free(gbVram); rlm@33: gbVram = NULL; rlm@33: } rlm@33: rlm@33: if (gbWram != NULL) rlm@33: { rlm@33: free(gbWram); rlm@33: gbWram = NULL; rlm@33: } rlm@33: rlm@33: systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; rlm@33: rlm@33: memset(gbJoymask, 0, sizeof(gbJoymask)); rlm@33: // FIXME: horrible kludge rlm@33: memset(s_gbJoymask, 0, sizeof(s_gbJoymask)); rlm@33: rlm@33: systemClearJoypads(); rlm@33: systemResetSensor(); rlm@33: rlm@33: // gbLastTime = gbFrameCount = 0; rlm@33: systemRefreshScreen(); rlm@1: } rlm@1: rlm@1: bool gbLoadRom(const char *szFile) rlm@1: { rlm@33: int size = 0; rlm@33: rlm@33: if (gbRom != NULL) rlm@33: { rlm@33: gbCleanUp(); rlm@33: } rlm@33: rlm@33: systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; rlm@33: rlm@33: gbRom = utilLoad(szFile, rlm@33: utilIsGBImage, rlm@33: NULL, rlm@33: size); rlm@33: if (!gbRom) rlm@33: return false; rlm@33: rlm@33: gbRomSize = size; rlm@33: rlm@33: return gbUpdateSizes(); rlm@1: } rlm@1: rlm@1: bool gbUpdateSizes() rlm@1: { rlm@33: if (gbRom[0x148] > 8) rlm@33: { rlm@33: systemMessage(MSG_UNSUPPORTED_ROM_SIZE, rlm@33: N_("Unsupported rom size %02x"), gbRom[0x148]); rlm@33: return false; rlm@33: } rlm@33: rlm@33: if (gbRomSize < gbRomSizes[gbRom[0x148]]) rlm@33: { rlm@33: gbRom = (u8 *)realloc(gbRom, gbRomSizes[gbRom[0x148]]); rlm@33: } rlm@33: gbRomSize = gbRomSizes[gbRom[0x148]]; rlm@33: gbRomSizeMask = gbRomSizesMasks[gbRom[0x148]]; rlm@33: rlm@33: if (gbRom[0x149] > 5) rlm@33: { rlm@33: systemMessage(MSG_UNSUPPORTED_RAM_SIZE, rlm@33: N_("Unsupported ram size %02x"), gbRom[0x149]); rlm@33: return false; rlm@33: } rlm@33: rlm@33: gbRamSize = gbRamSizes[gbRom[0x149]]; rlm@33: gbRamSizeMask = gbRamSizesMasks[gbRom[0x149]]; rlm@33: rlm@33: if (gbRamSize) rlm@33: { rlm@33: gbRam = (u8 *)malloc(gbRamSize + 4); rlm@33: memset(gbRam, 0xFF, gbRamSize + 4); rlm@33: } rlm@33: rlm@33: int type = gbRom[0x147]; rlm@33: rlm@33: mapperReadRAM = NULL; rlm@33: rlm@33: switch (type) rlm@33: { rlm@33: case 0x00: rlm@33: case 0x01: rlm@33: case 0x02: rlm@33: case 0x03: rlm@33: // MBC 1 rlm@33: mapper = mapperMBC1ROM; rlm@33: mapperRAM = mapperMBC1RAM; rlm@33: break; rlm@33: case 0x05: rlm@33: case 0x06: rlm@33: // MBC2 rlm@33: mapper = mapperMBC2ROM; rlm@33: mapperRAM = mapperMBC2RAM; rlm@33: gbRamSize = 0x200; rlm@33: gbRamSizeMask = 0x1ff; rlm@33: break; rlm@33: case 0x0f: rlm@33: case 0x10: rlm@33: case 0x11: rlm@33: case 0x12: rlm@33: case 0x13: rlm@33: // MBC 3 rlm@33: mapper = mapperMBC3ROM; rlm@33: mapperRAM = mapperMBC3RAM; rlm@33: mapperReadRAM = mapperMBC3ReadRAM; rlm@33: break; rlm@33: case 0x19: rlm@33: case 0x1a: rlm@33: case 0x1b: rlm@33: // MBC5 rlm@33: mapper = mapperMBC5ROM; rlm@33: mapperRAM = mapperMBC5RAM; rlm@33: break; rlm@33: case 0x1c: rlm@33: case 0x1d: rlm@33: case 0x1e: rlm@33: // MBC 5 Rumble rlm@33: mapper = mapperMBC5ROM; rlm@33: mapperRAM = mapperMBC5RAM; rlm@33: break; rlm@33: case 0x22: rlm@33: // MBC 7 rlm@33: mapper = mapperMBC7ROM; rlm@33: mapperRAM = mapperMBC7RAM; rlm@33: mapperReadRAM = mapperMBC7ReadRAM; rlm@33: break; rlm@33: case 0xfe: rlm@33: // HuC3 rlm@33: mapper = mapperHuC3ROM; rlm@33: mapperRAM = mapperHuC3RAM; rlm@33: mapperReadRAM = mapperHuC3ReadRAM; rlm@33: break; rlm@33: case 0xff: rlm@33: // HuC1 rlm@33: mapper = mapperHuC1ROM; rlm@33: mapperRAM = mapperHuC1RAM; rlm@33: break; rlm@33: default: rlm@33: systemMessage(MSG_UNKNOWN_CARTRIDGE_TYPE, rlm@33: N_("Unknown cartridge type %02x"), type); rlm@33: return false; rlm@33: } rlm@33: rlm@33: switch (type) rlm@33: { rlm@33: case 0x03: rlm@33: case 0x06: rlm@33: case 0x0f: rlm@33: case 0x10: rlm@33: case 0x13: rlm@33: case 0x1b: rlm@33: case 0x1d: rlm@33: case 0x1e: rlm@33: case 0x22: rlm@33: case 0xff: rlm@33: gbBattery = 1; rlm@33: break; rlm@33: } rlm@33: rlm@33: gbInit(); rlm@33: gbReset(); rlm@33: rlm@33: return true; rlm@1: } rlm@1: rlm@1: void gbEmulate(int ticksToStop) rlm@1: { rlm@39: //printf("RLM: Inside the GB!\n"); rlm@33: gbRegister tempRegister; rlm@33: u8 tempValue; rlm@33: s8 offset; rlm@33: rlm@33: int clockTicks = 0; rlm@33: gbDmaTicks = 0; rlm@33: rlm@33: register int opcode = 0; rlm@33: rlm@33: u32 newmask = 0; rlm@39: //printf("RLM: newframe = %d\n", newFrame); rlm@33: if (newFrame) rlm@33: { rlm@33: extern void VBAOnExitingFrameBoundary(); rlm@33: VBAOnExitingFrameBoundary(); rlm@39: //printf("RLM: exiting frame boundary?\n"); rlm@33: // update joystick information rlm@33: systemReadJoypads(); rlm@33: rlm@33: bool sensor = (gbRom[0x147] == 0x22); rlm@33: rlm@33: // read joystick rlm@33: if (gbSgbMode && gbSgbMultiplayer) rlm@1: { rlm@33: if (gbSgbFourPlayers) rlm@33: { rlm@33: gbJoymask[0] = systemGetJoypad(0, sensor); rlm@33: gbJoymask[1] = systemGetJoypad(1, false); rlm@33: gbJoymask[2] = systemGetJoypad(2, false); rlm@33: gbJoymask[3] = systemGetJoypad(3, false); rlm@33: } rlm@33: else rlm@33: { rlm@33: gbJoymask[0] = systemGetJoypad(0, sensor); rlm@33: gbJoymask[1] = systemGetJoypad(1, false); rlm@33: } rlm@33: } rlm@33: else rlm@33: { rlm@33: gbJoymask[0] = systemGetJoypad(0, sensor); rlm@33: } rlm@33: rlm@33: // FIXME: horrible kludge rlm@33: memcpy(s_gbJoymask, gbJoymask, sizeof(gbJoymask)); rlm@33: rlm@33: // if (sensor) rlm@33: // systemUpdateMotionSensor(0); rlm@33: rlm@33: newmask = gbJoymask[0]; rlm@33: if (newmask & 0xFF) rlm@33: { rlm@33: gbInterrupt |= 16; rlm@33: } rlm@33: rlm@33: extButtons = (newmask >> 18); rlm@33: speedup = (extButtons & 1) != 0; rlm@33: rlm@33: VBAMovieResetIfRequested(); rlm@39: //printf("RLM: before Lua functions\n"); rlm@33: //CallRegisteredLuaFunctions(LUACALL_BEFOREEMULATION); rlm@39: //printf("RLM: after Lua functions\n"); rlm@33: newFrame = false; rlm@33: } rlm@33: rlm@33: rlm@33: for (;; ) rlm@33: { rlm@33: #ifndef FINAL_VERSION rlm@33: if (systemDebug) rlm@33: { rlm@33: if (!(IFF & 0x80)) rlm@33: { rlm@33: if (systemDebug > 1) rlm@1: { rlm@33: sprintf(gbBuffer, "PC=%04x AF=%04x BC=%04x DE=%04x HL=%04x SP=%04x I=%04x\n", rlm@33: PC.W, AF.W, BC.W, DE.W, HL.W, SP.W, IFF); rlm@33: } rlm@33: else rlm@33: { rlm@33: sprintf(gbBuffer, "PC=%04x I=%02x\n", PC.W, IFF); rlm@33: } rlm@33: log(gbBuffer); rlm@33: } rlm@33: } rlm@33: #endif rlm@33: if (IFF & 0x80) rlm@33: { rlm@33: if (register_LCDC & 0x80) rlm@33: { rlm@33: clockTicks = gbLcdTicks; rlm@33: } rlm@33: else rlm@33: clockTicks = 100; rlm@33: rlm@33: if (gbLcdMode == 1 && (gbLcdLYIncrementTicks < clockTicks)) rlm@33: clockTicks = gbLcdLYIncrementTicks; rlm@33: rlm@33: if (gbSerialOn && (gbSerialTicks < clockTicks)) rlm@33: clockTicks = gbSerialTicks; rlm@33: rlm@33: if (gbTimerOn && (gbTimerTicks < clockTicks)) rlm@33: clockTicks = gbTimerTicks; rlm@33: rlm@33: if (soundTicks && (soundTicks < clockTicks)) rlm@33: clockTicks = soundTicks; rlm@33: } rlm@33: else rlm@33: { rlm@33: opcode = gbReadOpcode(PC.W); rlm@33: CallRegisteredLuaMemHook(PC.W, 1, opcode, LUAMEMHOOK_EXEC); rlm@33: PC.W++; rlm@33: rlm@33: if (IFF & 0x100) rlm@33: { rlm@33: IFF &= 0xff; rlm@33: PC.W--; rlm@33: } rlm@33: rlm@33: clockTicks = gbCycles[opcode]; rlm@33: rlm@33: switch (opcode) rlm@33: { rlm@33: case 0xCB: rlm@33: // extended opcode rlm@33: //CallRegisteredLuaMemHook(PC.W, 1, opcode, LUAMEMHOOK_EXEC); // is this desired? rlm@33: opcode = gbReadOpcode(PC.W++); rlm@33: clockTicks = gbCyclesCB[opcode]; rlm@33: switch (opcode) rlm@33: { rlm@33: #include "gbCodesCB.h" rlm@33: } rlm@33: break; rlm@33: #include "gbCodes.h" rlm@33: } rlm@33: } rlm@33: rlm@33: if (!emulating) rlm@33: return; rlm@33: rlm@33: if (gbDmaTicks) rlm@33: { rlm@33: clockTicks += gbDmaTicks; rlm@33: gbDmaTicks = 0; rlm@33: } rlm@33: rlm@33: if (gbSgbMode) rlm@33: { rlm@33: if (gbSgbPacketTimeout) rlm@33: { rlm@33: gbSgbPacketTimeout -= clockTicks; rlm@33: rlm@33: if (gbSgbPacketTimeout <= 0) rlm@33: gbSgbResetPacketState(); rlm@33: } rlm@33: } rlm@33: rlm@33: ticksToStop -= clockTicks; rlm@33: rlm@33: // DIV register emulation rlm@33: gbDivTicks -= clockTicks; rlm@33: while (gbDivTicks <= 0) rlm@33: { rlm@33: register_DIV++; rlm@33: gbDivTicks += GBDIV_CLOCK_TICKS; rlm@33: } rlm@33: rlm@33: if (register_LCDC & 0x80) rlm@33: { rlm@33: // LCD stuff rlm@33: gbLcdTicks -= clockTicks; rlm@33: if (gbLcdMode == 1) rlm@33: { rlm@33: // during V-BLANK,we need to increment LY at the same rate! rlm@33: gbLcdLYIncrementTicks -= clockTicks; rlm@33: while (gbLcdLYIncrementTicks <= 0) rlm@33: { rlm@33: gbLcdLYIncrementTicks += GBLY_INCREMENT_CLOCK_TICKS; rlm@33: rlm@33: if (register_LY < 153) rlm@33: { rlm@33: register_LY++; rlm@33: rlm@33: gbCompareLYToLYC(); rlm@33: rlm@33: if (register_LY >= 153) rlm@33: gbLcdLYIncrementTicks = 6; rlm@33: } rlm@33: else rlm@33: { rlm@33: register_LY = 0x00; rlm@33: // reset the window line rlm@33: gbWindowLine = -1; rlm@33: gbLcdLYIncrementTicks = GBLY_INCREMENT_CLOCK_TICKS * 2; rlm@33: gbCompareLYToLYC(); rlm@33: } rlm@33: } rlm@33: } rlm@33: rlm@33: // our counter is off, see what we need to do rlm@33: while (gbLcdTicks <= 0) rlm@33: { rlm@33: int framesToSkip = systemFramesToSkip(); rlm@33: rlm@33: switch (gbLcdMode) rlm@33: { rlm@33: case 0: rlm@33: // H-Blank rlm@33: register_LY++; rlm@33: rlm@33: gbCompareLYToLYC(); rlm@33: rlm@33: // check if we reached the V-Blank period rlm@33: if (register_LY == 144) rlm@33: { rlm@33: // Yes, V-Blank rlm@33: // set the LY increment counter rlm@33: gbLcdLYIncrementTicks = gbLcdTicks + GBLY_INCREMENT_CLOCK_TICKS; rlm@33: gbLcdTicks += GBLCD_MODE_1_CLOCK_TICKS; rlm@33: gbLcdMode = 1; rlm@33: if (register_LCDC & 0x80) rlm@1: { rlm@33: gbInterrupt |= 1; // V-Blank interrupt rlm@33: gbInterruptWait = 6; rlm@33: if (register_STAT & 0x10) rlm@33: gbInterrupt |= 2; rlm@1: } rlm@33: rlm@33: systemFrame(); rlm@33: rlm@33: ++gbFrameCount; rlm@33: u32 currentTime = systemGetClock(); rlm@33: if (currentTime - gbLastTime >= 1000) rlm@1: { rlm@33: systemShowSpeed(int(float(gbFrameCount) * 100000 / (float(currentTime - gbLastTime) * 60) + .5f)); rlm@33: gbLastTime = currentTime; rlm@33: gbFrameCount = 0; rlm@1: } rlm@33: rlm@33: ++GBSystemCounters.frameCount; rlm@33: if (GBSystemCounters.lagged) rlm@33: { rlm@33: ++GBSystemCounters.lagCount; rlm@33: } rlm@33: GBSystemCounters.laggedLast = GBSystemCounters.lagged; rlm@33: GBSystemCounters.lagged = true; rlm@33: rlm@33: extern void VBAOnEnteringFrameBoundary(); rlm@33: VBAOnEnteringFrameBoundary(); rlm@33: rlm@33: newFrame = true; rlm@33: rlm@33: pauseAfterFrameAdvance = systemPauseOnFrame(); rlm@33: rlm@33: if (gbFrameSkipCount >= framesToSkip || pauseAfterFrameAdvance) rlm@33: { rlm@33: if (gbBorderOn) rlm@33: gbSgbRenderBorder(); // clear unnecessary things on border (e.g. in-game text message) rlm@33: rlm@33: systemRenderFrame(); rlm@33: gbFrameSkipCount = 0; rlm@33: rlm@33: bool capturePressed = (extButtons & 2) != 0; rlm@33: if (capturePressed && !capturePrevious) rlm@33: { rlm@33: captureNumber = systemScreenCapture(captureNumber); rlm@33: } rlm@33: capturePrevious = capturePressed && !pauseAfterFrameAdvance; rlm@33: } rlm@33: else rlm@33: { rlm@33: ++gbFrameSkipCount; rlm@33: } rlm@33: rlm@33: if (pauseAfterFrameAdvance) rlm@33: { rlm@33: systemSetPause(true); rlm@33: } rlm@33: } rlm@33: else rlm@33: { rlm@33: // go the the OAM being accessed mode rlm@33: gbLcdTicks += GBLCD_MODE_2_CLOCK_TICKS; rlm@33: gbLcdMode = 2; rlm@33: rlm@33: // only one LCD interrupt per line. may need to generalize... rlm@33: if (!(register_STAT & 0x40) || rlm@33: (register_LY != register_LYC)) rlm@33: { rlm@33: if ((register_STAT & 0x28) == 0x20) rlm@33: gbInterrupt |= 2; rlm@33: } rlm@33: } rlm@33: rlm@33: break; rlm@33: case 1: rlm@33: // V-Blank rlm@33: // next mode is OAM being accessed mode rlm@33: gbLcdTicks += GBLCD_MODE_2_CLOCK_TICKS; rlm@33: gbLcdMode = 2; rlm@33: if (!(register_STAT & 0x40) || rlm@33: (register_LY != register_LYC)) rlm@33: { rlm@33: if ((register_STAT & 0x28) == 0x20) rlm@33: gbInterrupt |= 2; rlm@33: } rlm@33: break; rlm@33: case 2: rlm@33: // OAM being accessed mode rlm@33: rlm@33: // next mode is OAM and VRAM in use rlm@33: gbLcdTicks += GBLCD_MODE_3_CLOCK_TICKS; rlm@33: gbLcdMode = 3; rlm@33: break; rlm@33: case 3: rlm@33: // OAM and VRAM in use rlm@33: // next mode is H-Blank rlm@33: if (register_LY < 144) rlm@33: { rlm@33: if (!gbSgbMask) rlm@33: { rlm@33: if (gbFrameSkipCount >= framesToSkip || pauseAfterFrameAdvance) rlm@33: { rlm@33: gbRenderLine(); rlm@33: gbDrawSprites(); rlm@33: rlm@33: switch (systemColorDepth) rlm@33: { rlm@33: case 16: rlm@33: rlm@33: { rlm@33: u16 *dest = (u16 *)pix + rlm@33: (gbBorderLineSkip + 2) * (register_LY + gbBorderRowSkip + 1) rlm@33: + gbBorderColumnSkip; rlm@33: for (int x = 0; x < 160; ) rlm@33: { rlm@33: *dest++ = systemColorMap16[gbLineMix[x++]]; rlm@33: *dest++ = systemColorMap16[gbLineMix[x++]]; rlm@33: *dest++ = systemColorMap16[gbLineMix[x++]]; rlm@33: *dest++ = systemColorMap16[gbLineMix[x++]]; rlm@33: rlm@33: *dest++ = systemColorMap16[gbLineMix[x++]]; rlm@33: *dest++ = systemColorMap16[gbLineMix[x++]]; rlm@33: *dest++ = systemColorMap16[gbLineMix[x++]]; rlm@33: *dest++ = systemColorMap16[gbLineMix[x++]]; rlm@33: rlm@33: *dest++ = systemColorMap16[gbLineMix[x++]]; rlm@33: *dest++ = systemColorMap16[gbLineMix[x++]]; rlm@33: *dest++ = systemColorMap16[gbLineMix[x++]]; rlm@33: *dest++ = systemColorMap16[gbLineMix[x++]]; rlm@33: rlm@33: *dest++ = systemColorMap16[gbLineMix[x++]]; rlm@33: *dest++ = systemColorMap16[gbLineMix[x++]]; rlm@33: *dest++ = systemColorMap16[gbLineMix[x++]]; rlm@33: *dest++ = systemColorMap16[gbLineMix[x++]]; rlm@33: } rlm@33: if (gbBorderOn) rlm@33: dest += gbBorderColumnSkip; rlm@33: *dest++ = 0; // for filters that read one pixel more rlm@33: break; rlm@33: } rlm@33: case 24: rlm@33: rlm@33: { rlm@33: u8 *dest = (u8 *)pix + rlm@33: 3 * (gbBorderLineSkip * (register_LY + gbBorderRowSkip) + rlm@33: gbBorderColumnSkip); rlm@33: for (int x = 0; x < 160; ) rlm@33: { rlm@33: *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; rlm@33: dest += 3; rlm@33: *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; rlm@33: dest += 3; rlm@33: *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; rlm@33: dest += 3; rlm@33: *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; rlm@33: dest += 3; rlm@33: rlm@33: *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; rlm@33: dest += 3; rlm@33: *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; rlm@33: dest += 3; rlm@33: *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; rlm@33: dest += 3; rlm@33: *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; rlm@33: dest += 3; rlm@33: rlm@33: *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; rlm@33: dest += 3; rlm@33: *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; rlm@33: dest += 3; rlm@33: *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; rlm@33: dest += 3; rlm@33: *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; rlm@33: dest += 3; rlm@33: rlm@33: *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; rlm@33: dest += 3; rlm@33: *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; rlm@33: dest += 3; rlm@33: *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; rlm@33: dest += 3; rlm@33: *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; rlm@33: dest += 3; rlm@33: } rlm@33: break; rlm@33: } rlm@33: case 32: rlm@33: rlm@33: { rlm@33: u32 *dest = (u32 *)pix + rlm@33: (gbBorderLineSkip + 1) * (register_LY + gbBorderRowSkip + 1) rlm@33: + gbBorderColumnSkip; rlm@33: for (int x = 0; x < 160; ) rlm@33: { rlm@33: *dest++ = systemColorMap32[gbLineMix[x++]]; rlm@33: *dest++ = systemColorMap32[gbLineMix[x++]]; rlm@33: *dest++ = systemColorMap32[gbLineMix[x++]]; rlm@33: *dest++ = systemColorMap32[gbLineMix[x++]]; rlm@33: rlm@33: *dest++ = systemColorMap32[gbLineMix[x++]]; rlm@33: *dest++ = systemColorMap32[gbLineMix[x++]]; rlm@33: *dest++ = systemColorMap32[gbLineMix[x++]]; rlm@33: *dest++ = systemColorMap32[gbLineMix[x++]]; rlm@33: rlm@33: *dest++ = systemColorMap32[gbLineMix[x++]]; rlm@33: *dest++ = systemColorMap32[gbLineMix[x++]]; rlm@33: *dest++ = systemColorMap32[gbLineMix[x++]]; rlm@33: *dest++ = systemColorMap32[gbLineMix[x++]]; rlm@33: rlm@33: *dest++ = systemColorMap32[gbLineMix[x++]]; rlm@33: *dest++ = systemColorMap32[gbLineMix[x++]]; rlm@33: *dest++ = systemColorMap32[gbLineMix[x++]]; rlm@33: *dest++ = systemColorMap32[gbLineMix[x++]]; rlm@33: } rlm@33: break; rlm@33: } rlm@33: } rlm@33: } rlm@33: } rlm@33: } rlm@33: gbLcdTicks += GBLCD_MODE_0_CLOCK_TICKS; rlm@33: gbLcdMode = 0; rlm@33: // only one LCD interrupt per line. may need to generalize... rlm@33: if (!(register_STAT & 0x40) || rlm@33: (register_LY != register_LYC)) rlm@33: { rlm@33: if (register_STAT & 0x08) rlm@33: gbInterrupt |= 2; rlm@33: } rlm@33: if (gbHdmaOn) rlm@33: { rlm@33: gbDoHdma(); rlm@33: } rlm@33: break; rlm@1: } rlm@33: // mark the correct lcd mode on STAT register rlm@33: register_STAT = (register_STAT & 0xfc) | gbLcdMode; rlm@33: } rlm@33: } rlm@33: rlm@33: // serial emulation rlm@33: if (gbSerialOn) rlm@33: { rlm@33: #ifdef LINK_EMULATION rlm@33: if (linkConnected) rlm@33: { rlm@33: gbSerialTicks -= clockTicks; rlm@33: rlm@33: while (gbSerialTicks <= 0) rlm@1: { rlm@33: // increment number of shifted bits rlm@33: gbSerialBits++; rlm@33: linkProc(); rlm@33: if (gbSerialOn && (gbMemory[0xff02] & 1)) rlm@33: { rlm@33: if (gbSerialBits == 8) rlm@33: { rlm@33: gbSerialBits = 0; rlm@33: gbMemory[0xff01] = 0xff; rlm@33: gbMemory[0xff02] &= 0x7f; rlm@33: gbSerialOn = 0; rlm@33: gbInterrupt |= 8; rlm@33: gbSerialTicks = 0; rlm@33: } rlm@33: } rlm@33: gbSerialTicks += GBSERIAL_CLOCK_TICKS; rlm@1: } rlm@33: } rlm@33: else rlm@33: { rlm@33: #endif rlm@33: if (gbMemory[0xff02] & 1) rlm@1: { rlm@33: gbSerialTicks -= clockTicks; rlm@33: rlm@33: // overflow rlm@33: while (gbSerialTicks <= 0) rlm@33: { rlm@33: // shift serial byte to right and put a 1 bit in its place rlm@33: // gbMemory[0xff01] = 0x80 | (gbMemory[0xff01]>>1); rlm@33: // increment number of shifted bits rlm@33: gbSerialBits++; rlm@33: if (gbSerialBits == 8) rlm@33: { rlm@33: // end of transmission rlm@33: if (gbSerialFunction) // external device rlm@33: gbMemory[0xff01] = gbSerialFunction(gbMemory[0xff01]); rlm@33: else rlm@33: gbMemory[0xff01] = 0xff; rlm@33: gbSerialTicks = 0; rlm@33: gbMemory[0xff02] &= 0x7f; rlm@33: gbSerialOn = 0; rlm@33: gbInterrupt |= 8; rlm@33: gbSerialBits = 0; rlm@33: } rlm@33: else rlm@33: gbSerialTicks += GBSERIAL_CLOCK_TICKS; rlm@33: } rlm@1: } rlm@33: #ifdef LINK_EMULATION rlm@33: } rlm@33: #endif rlm@1: } rlm@1: rlm@33: // timer emulation rlm@33: if (gbTimerOn) rlm@1: { rlm@33: gbTimerTicks -= clockTicks; rlm@33: rlm@33: while (gbTimerTicks <= 0) rlm@33: { rlm@33: register_TIMA++; rlm@33: rlm@33: if (register_TIMA == 0) rlm@1: { rlm@33: // timer overflow! rlm@33: rlm@33: // reload timer modulo rlm@33: register_TIMA = register_TMA; rlm@33: rlm@33: // flag interrupt rlm@33: gbInterrupt |= 4; rlm@33: } rlm@33: rlm@33: gbTimerTicks += gbTimerClockTicks; rlm@33: } rlm@33: } rlm@33: rlm@33: /* rlm@33: if(soundOffFlag) rlm@33: { rlm@33: if(synchronize && !speedup) rlm@33: { rlm@33: synchronizeTicks -= clockTicks; rlm@33: rlm@33: while(synchronizeTicks < 0) rlm@33: { rlm@33: synchronizeTicks += SYNCHRONIZE_CLOCK_TICKS; rlm@33: rlm@33: DWORD now = timeGetTime(); rlm@33: gbElapsedTime += (now - timeNow); rlm@33: rlm@33: if(gbElapsedTime < 50) rlm@33: { rlm@33: DWORD diff = 50 - gbElapsedTime; rlm@33: Sleep(diff); rlm@33: timeNow = timeGetTime(); rlm@33: elapsedTime = timeNow - now - diff; rlm@33: if((int)elapsedTime < 0) rlm@33: elapsedTime = 0; rlm@33: } else rlm@33: { rlm@33: timeNow = timeGetTime(); rlm@33: elapsedTime = 0; rlm@33: } rlm@33: } rlm@33: } rlm@33: } rlm@33: */ rlm@33: rlm@33: soundTicks -= clockTicks; rlm@33: while (soundTicks < 0) // must be < 1 when soundtick_t is real data type rlm@33: { rlm@33: soundTicks += SOUND_CLOCK_TICKS; rlm@33: rlm@33: gbSoundTick(); rlm@33: } rlm@33: rlm@33: register_IF = gbInterrupt; rlm@33: rlm@33: if (IFF & 0x20) rlm@33: { rlm@33: IFF &= 0xdf; rlm@33: IFF |= 0x01; rlm@33: gbInterruptWait = 0; rlm@33: } rlm@33: else if (gbInterrupt) rlm@33: { rlm@33: if (gbInterruptWait == 0) rlm@33: { rlm@33: // gbInterruptWait = 0; rlm@33: rlm@33: if (IFF & 0x01) rlm@33: { rlm@33: if ((gbInterrupt & 1) && (register_IE & 1)) rlm@33: { rlm@33: gbVblank_interrupt(); rlm@33: continue; rlm@33: } rlm@33: rlm@33: if ((gbInterrupt & 2) && (register_IE & 2)) rlm@33: { rlm@33: gbLcd_interrupt(); rlm@33: continue; rlm@33: } rlm@33: rlm@33: if ((gbInterrupt & 4) && (register_IE & 4)) rlm@33: { rlm@33: gbTimer_interrupt(); rlm@33: continue; rlm@33: } rlm@33: rlm@33: if ((gbInterrupt & 8) && (register_IE & 8)) rlm@33: { rlm@33: gbSerial_interrupt(); rlm@33: continue; rlm@33: } rlm@33: rlm@33: if ((gbInterrupt & 16) && (register_IE & 16)) rlm@33: { rlm@33: gbJoypad_interrupt(); rlm@33: continue; rlm@33: } rlm@33: } rlm@33: } rlm@33: else rlm@33: { rlm@33: gbInterruptWait -= clockTicks; rlm@33: if (gbInterruptWait < 0) rlm@33: gbInterruptWait = 0; rlm@33: } rlm@33: } rlm@33: rlm@33: if (useOldFrameTiming) rlm@33: { rlm@33: // old timing code rlm@33: if (ticksToStop > 0) rlm@33: continue; rlm@33: } rlm@33: else rlm@33: { rlm@33: if (!newFrame && (register_LCDC & 0x80) != 0) rlm@33: continue; rlm@33: } rlm@33: rlm@33: if (!(register_LCDC & 0x80)) rlm@33: { rlm@33: if (!useOldFrameTiming) rlm@33: { rlm@33: // FIXME: since register_LY can be reset to 0 by some games, frame length is variable rlm@33: // and infinite loops can occurr rlm@33: // for now, it IS necessary to do something on this condition or games like rlm@33: // Megaman would freeze upon low-level restart interrupt sequence (Start+Select+A+B). rlm@33: // the only sensible way to fix this issue is to implement the RIGHT frame timing rlm@33: #ifdef WANTS_INCOMPLETE_WORKAROUND rlm@33: if (systemReadJoypads()) rlm@33: { rlm@33: if (gbSgbMode && gbSgbMultiplayer) rlm@33: { rlm@33: if (gbSgbFourPlayers) rlm@1: { rlm@33: gbJoymask[0] = systemGetJoypad(0, false); rlm@33: gbJoymask[1] = systemGetJoypad(1, false); rlm@33: gbJoymask[2] = systemGetJoypad(2, false); rlm@33: gbJoymask[3] = systemGetJoypad(3, false); rlm@1: } rlm@33: else rlm@33: { rlm@33: gbJoymask[0] = systemGetJoypad(0, false); rlm@33: gbJoymask[1] = systemGetJoypad(1, false); rlm@33: } rlm@33: } rlm@33: else rlm@33: { rlm@33: gbJoymask[0] = systemGetJoypad(0, false); rlm@33: } rlm@1: } rlm@33: else rlm@33: gbJoymask[0] = gbJoymask[1] = gbJoymask[2] = gbJoymask[3] = 0; rlm@1: #else rlm@1: #endif rlm@33: } rlm@1: } rlm@33: rlm@33: // makes sure frames are really divided across input sampling boundaries which occur at a constant rate rlm@33: if (newFrame || useOldFrameTiming) rlm@33: { rlm@33: /// extern void VBAOnEnteringFrameBoundary(); rlm@33: /// VBAOnEnteringFrameBoundary(); rlm@33: rlm@33: break; rlm@33: } rlm@33: } rlm@1: } rlm@1: rlm@1: struct EmulatedSystem GBSystem = rlm@33: { rlm@33: // emuMain rlm@33: gbEmulate, rlm@33: // emuReset rlm@33: gbReset, rlm@33: // emuCleanUp rlm@33: gbCleanUp, rlm@33: // emuReadBattery rlm@33: gbReadBatteryFile, rlm@33: // emuWriteBattery rlm@33: gbWriteBatteryFile, rlm@33: // emuReadBatteryFromStream rlm@33: gbReadBatteryFromStream, rlm@33: // emuWriteBatteryToStream rlm@33: gbWriteBatteryToStream, rlm@33: // emuReadState rlm@33: gbReadSaveState, rlm@33: // emuWriteState rlm@33: gbWriteSaveState, rlm@33: // emuReadStateFromStream rlm@33: gbReadSaveStateFromStream, rlm@33: // emuWriteStateToStream rlm@33: gbWriteSaveStateToStream, rlm@33: // emuReadMemState rlm@33: gbReadMemSaveState, rlm@33: // emuWriteMemState rlm@33: gbWriteMemSaveState, rlm@33: // emuWritePNG rlm@33: gbWritePNGFile, rlm@33: // emuWriteBMP rlm@33: gbWriteBMPFile, rlm@33: // emuUpdateCPSR rlm@33: NULL, rlm@33: // emuHasDebugger rlm@33: false, rlm@33: // emuCount rlm@1: #ifdef FINAL_VERSION rlm@33: 70000 / 4, rlm@1: #else rlm@33: 1000, rlm@1: #endif rlm@33: }; rlm@1: rlm@1: // is there a reason to use more than one set of counters? rlm@1: EmulatedSystemCounters &GBSystemCounters = systemCounters; rlm@1: rlm@1: /* rlm@33: EmulatedSystemCounters GBSystemCounters = rlm@33: { rlm@33: // frameCount rlm@33: 0, rlm@33: // lagCount rlm@33: 0, rlm@33: // lagged rlm@33: true, rlm@33: // laggedLast rlm@33: true, rlm@33: }; rlm@33: */