mirror of
https://github.com/dborth/vbagx.git
synced 2024-11-26 04:24:16 +01:00
8b49ff30cc
The old MBC2 saving routine based on gbMemory the savedata generated/stored on mapper MBC2 to not save correctly. The affected games by this includes F-1 Race, Kirby's Pinball Land, etc. This fixes said issue, by replacing the save routine from gbMemory to gbRam, like the other mapper saving routines. Also changes the savefile size to 512 kB for avoid saving issues with all MBC2 games. Based on the same fix by Steelskin.
5847 lines
162 KiB
C++
5847 lines
162 KiB
C++
//#include "../win32/stdafx.h" // would fix LNK2005 linker errors for MSVC
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <algorithm>
|
|
|
|
#include "../System.h"
|
|
#include "../NLS.h"
|
|
#include "gb.h"
|
|
#include "gbCheats.h"
|
|
#include "gbGlobals.h"
|
|
#include "gbMemory.h"
|
|
#include "gbSGB.h"
|
|
#include "gbSound.h"
|
|
#include "../Util.h"
|
|
|
|
#ifdef __GNUC__
|
|
#define _stricmp strcasecmp
|
|
#endif
|
|
|
|
void gbSetBGPalette(u8 value, bool ColoursChanged=false);
|
|
void gbSetObj0Palette(u8 value, bool ColoursChanged=false);
|
|
void gbSetObj1Palette(u8 value, bool ColoursChanged=false);
|
|
void gbPaletteReset();
|
|
|
|
extern u8 *pix;
|
|
extern bool speedup;
|
|
bool gbUpdateSizes();
|
|
bool inBios = false;
|
|
|
|
// debugging
|
|
bool memorydebug = false;
|
|
char gbBuffer[2048];
|
|
|
|
extern u16 gbLineMix[160];
|
|
|
|
// mappers
|
|
void (*mapper)(u16,u8) = NULL;
|
|
void (*mapperRAM)(u16,u8) = NULL;
|
|
u8 (*mapperReadRAM)(u16) = NULL;
|
|
void (*mapperUpdateClock)() = NULL;
|
|
|
|
// registers
|
|
gbRegister PC;
|
|
gbRegister SP;
|
|
gbRegister AF;
|
|
gbRegister BC;
|
|
gbRegister DE;
|
|
gbRegister HL;
|
|
u16 IFF = 0;
|
|
// 0xff04
|
|
u8 register_DIV = 0;
|
|
// 0xff05
|
|
u8 register_TIMA = 0;
|
|
// 0xff06
|
|
u8 register_TMA = 0;
|
|
// 0xff07
|
|
u8 register_TAC = 0;
|
|
// 0xff0f
|
|
u8 register_IF = 0;
|
|
// 0xff40
|
|
u8 register_LCDC = 0;
|
|
// 0xff41
|
|
u8 register_STAT = 0;
|
|
// 0xff42
|
|
u8 register_SCY = 0;
|
|
// 0xff43
|
|
u8 register_SCX = 0;
|
|
// 0xff44
|
|
u8 register_LY = 0;
|
|
// 0xff45
|
|
u8 register_LYC = 0;
|
|
// 0xff46
|
|
u8 register_DMA = 0;
|
|
// 0xff4a
|
|
u8 register_WY = 0;
|
|
// 0xff4b
|
|
u8 register_WX = 0;
|
|
// 0xff4f
|
|
u8 register_VBK = 0;
|
|
// 0xff51
|
|
u8 register_HDMA1 = 0;
|
|
// 0xff52
|
|
u8 register_HDMA2 = 0;
|
|
// 0xff53
|
|
u8 register_HDMA3 = 0;
|
|
// 0xff54
|
|
u8 register_HDMA4 = 0;
|
|
// 0xff55
|
|
u8 register_HDMA5 = 0;
|
|
// 0xff70
|
|
u8 register_SVBK = 0;
|
|
// 0xffff
|
|
u8 register_IE = 0;
|
|
|
|
// ticks definition
|
|
int GBDIV_CLOCK_TICKS = 64;
|
|
int GBLCD_MODE_0_CLOCK_TICKS = 51;
|
|
int GBLCD_MODE_1_CLOCK_TICKS = 1140;
|
|
int GBLCD_MODE_2_CLOCK_TICKS = 20;
|
|
int GBLCD_MODE_3_CLOCK_TICKS = 43;
|
|
int GBLY_INCREMENT_CLOCK_TICKS = 114;
|
|
int GBTIMER_MODE_0_CLOCK_TICKS = 256;
|
|
int GBTIMER_MODE_1_CLOCK_TICKS = 4;
|
|
int GBTIMER_MODE_2_CLOCK_TICKS = 16;
|
|
int GBTIMER_MODE_3_CLOCK_TICKS = 64;
|
|
int GBSERIAL_CLOCK_TICKS = 128;
|
|
int GBSYNCHRONIZE_CLOCK_TICKS = 52920;
|
|
|
|
// state variables
|
|
|
|
// general
|
|
int clockTicks = 0;
|
|
bool gbSystemMessage = false;
|
|
int gbGBCColorType = 0;
|
|
int gbHardware = 0;
|
|
int gbRomType = 0;
|
|
int gbRemainingClockTicks = 0;
|
|
int gbOldClockTicks = 0;
|
|
int gbIntBreak = 0;
|
|
int gbInterruptLaunched = 0;
|
|
u8 gbCheatingDevice = 0; // 1 = GS, 2 = GG
|
|
// breakpoint
|
|
bool breakpoint = false;
|
|
// interrupt
|
|
int gbInt48Signal = 0;
|
|
int gbInterruptWait = 0;
|
|
// serial
|
|
int gbSerialOn = 0;
|
|
int gbSerialTicks = 0;
|
|
int gbSerialBits = 0;
|
|
// timer
|
|
bool gbTimerOn = false;
|
|
int gbTimerTicks = GBTIMER_MODE_0_CLOCK_TICKS;
|
|
int gbTimerClockTicks = GBTIMER_MODE_0_CLOCK_TICKS;
|
|
int gbTimerMode = 0;
|
|
bool gbIncreased = false;
|
|
// The internal timer is always active, and it is
|
|
// not reset by writing to register_TIMA/TMA, but by
|
|
// writing to register_DIV...
|
|
int gbInternalTimer = 0x55;
|
|
const u8 gbTimerMask [4] = {0xff, 0x3, 0xf, 0x3f};
|
|
const u8 gbTimerBug [8] = {0x80, 0x80, 0x02, 0x02, 0x0, 0xff, 0x0, 0xff};
|
|
bool gbTimerModeChange = false;
|
|
bool gbTimerOnChange = false;
|
|
// lcd
|
|
bool gbScreenOn = true;
|
|
int gbLcdMode = 2;
|
|
int gbLcdModeDelayed = 2;
|
|
int gbLcdTicks = GBLCD_MODE_2_CLOCK_TICKS-1;
|
|
int gbLcdTicksDelayed = GBLCD_MODE_2_CLOCK_TICKS;
|
|
int gbLcdLYIncrementTicks = 114;
|
|
int gbLcdLYIncrementTicksDelayed = 115;
|
|
int gbScreenTicks = 0;
|
|
u8 gbSCYLine[300];
|
|
u8 gbSCXLine[300];
|
|
u8 gbBgpLine[300];
|
|
u8 gbObp0Line [300];
|
|
u8 gbObp1Line [300];
|
|
u8 gbSpritesTicks [300];
|
|
u8 oldRegister_WY;
|
|
bool gbLYChangeHappened = false;
|
|
bool gbLCDChangeHappened = false;
|
|
int gbLine99Ticks = 1;
|
|
int gbRegisterLYLCDCOffOn = 0;
|
|
int inUseRegister_WY = 0;
|
|
|
|
// Used to keep track of the line that ellapse
|
|
// when screen is off
|
|
int gbWhiteScreen = 0;
|
|
bool gbBlackScreen = false;
|
|
int register_LCDCBusy = 0;
|
|
|
|
// div
|
|
int gbDivTicks = GBDIV_CLOCK_TICKS;
|
|
// cgb
|
|
int gbVramBank = 0;
|
|
int gbWramBank = 1;
|
|
//sgb
|
|
bool gbSgbResetFlag = false;
|
|
// gbHdmaDestination is 0x99d0 on startup (tested on HW)
|
|
// but I'm not sure what gbHdmaSource is...
|
|
int gbHdmaSource = 0x99d0;
|
|
int gbHdmaDestination = 0x99d0;
|
|
int gbHdmaBytes = 0x0000;
|
|
int gbHdmaOn = 0;
|
|
int gbSpeed = 0;
|
|
// frame counting
|
|
int gbFrameCount = 0;
|
|
int gbFrameSkip = 0;
|
|
int gbFrameSkipCount = 0;
|
|
// timing
|
|
u32 gbLastTime = 0;
|
|
u32 gbElapsedTime = 0;
|
|
u32 gbTimeNow = 0;
|
|
int gbSynchronizeTicks = GBSYNCHRONIZE_CLOCK_TICKS;
|
|
// emulator features
|
|
int gbBattery = 0;
|
|
bool gbBatteryError = false;
|
|
int gbCaptureNumber = 0;
|
|
bool gbCapture = false;
|
|
bool gbCapturePrevious = false;
|
|
int gbJoymask[4] = { 0, 0, 0, 0 };
|
|
|
|
u8 gbRamFill = 0xff;
|
|
|
|
int gbRomSizes[] = { 0x00008000, // 32K
|
|
0x00010000, // 64K
|
|
0x00020000, // 128K
|
|
0x00040000, // 256K
|
|
0x00080000, // 512K
|
|
0x00100000, // 1024K
|
|
0x00200000, // 2048K
|
|
0x00400000, // 4096K
|
|
0x00800000 // 8192K
|
|
};
|
|
int gbRomSizesMasks[] = { 0x00007fff,
|
|
0x0000ffff,
|
|
0x0001ffff,
|
|
0x0003ffff,
|
|
0x0007ffff,
|
|
0x000fffff,
|
|
0x001fffff,
|
|
0x003fffff,
|
|
0x007fffff
|
|
};
|
|
|
|
int gbRamSizes[6] = { 0x00000000, // 0K
|
|
0x00002000, // 2K // Changed to 2000 to avoid problems with gbMemoryMap...
|
|
0x00002000, // 8K
|
|
0x00008000, // 32K
|
|
0x00020000, // 128K
|
|
0x00010000 // 64K
|
|
};
|
|
|
|
int gbRamSizesMasks[6] = { 0x00000000,
|
|
0x000007ff,
|
|
0x00001fff,
|
|
0x00007fff,
|
|
0x0001ffff,
|
|
0x0000ffff
|
|
};
|
|
|
|
int gbCycles[] = {
|
|
// 0 1 2 3 4 5 6 7 8 9 a b c d e f
|
|
1, 3, 2, 2, 1, 1, 2, 1, 5, 2, 2, 2, 1, 1, 2, 1, // 0
|
|
1, 3, 2, 2, 1, 1, 2, 1, 3, 2, 2, 2, 1, 1, 2, 1, // 1
|
|
2, 3, 2, 2, 1, 1, 2, 1, 2, 2, 2, 2, 1, 1, 2, 1, // 2
|
|
2, 3, 2, 2, 3, 3, 3, 1, 2, 2, 2, 2, 1, 1, 2, 1, // 3
|
|
1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 4
|
|
1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 5
|
|
1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 6
|
|
2, 2, 2, 2, 2, 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, // 7
|
|
1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 8
|
|
1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 9
|
|
1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // a
|
|
1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // b
|
|
2, 3, 3, 4, 3, 4, 2, 4, 2, 4, 3, 2, 3, 6, 2, 4, // c
|
|
2, 3, 3, 1, 3, 4, 2, 4, 2, 4, 3, 1, 3, 1, 2, 4, // d
|
|
3, 3, 2, 1, 1, 4, 2, 4, 4, 1, 4, 1, 1, 1, 2, 4, // e
|
|
3, 3, 2, 1, 1, 4, 2, 4, 3, 2, 4, 1, 0, 1, 2, 4 // f
|
|
};
|
|
|
|
int gbCyclesCB[] = {
|
|
// 0 1 2 3 4 5 6 7 8 9 a b c d e f
|
|
2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 0
|
|
2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 1
|
|
2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 2
|
|
2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 3
|
|
2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // 4
|
|
2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // 5
|
|
2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // 6
|
|
2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // 7
|
|
2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 8
|
|
2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 9
|
|
2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // a
|
|
2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // b
|
|
2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // c
|
|
2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // d
|
|
2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // e
|
|
2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2 // f
|
|
};
|
|
|
|
u16 DAATable[] = {
|
|
0x0080,0x0100,0x0200,0x0300,0x0400,0x0500,0x0600,0x0700,
|
|
0x0800,0x0900,0x1000,0x1100,0x1200,0x1300,0x1400,0x1500,
|
|
0x1000,0x1100,0x1200,0x1300,0x1400,0x1500,0x1600,0x1700,
|
|
0x1800,0x1900,0x2000,0x2100,0x2200,0x2300,0x2400,0x2500,
|
|
0x2000,0x2100,0x2200,0x2300,0x2400,0x2500,0x2600,0x2700,
|
|
0x2800,0x2900,0x3000,0x3100,0x3200,0x3300,0x3400,0x3500,
|
|
0x3000,0x3100,0x3200,0x3300,0x3400,0x3500,0x3600,0x3700,
|
|
0x3800,0x3900,0x4000,0x4100,0x4200,0x4300,0x4400,0x4500,
|
|
0x4000,0x4100,0x4200,0x4300,0x4400,0x4500,0x4600,0x4700,
|
|
0x4800,0x4900,0x5000,0x5100,0x5200,0x5300,0x5400,0x5500,
|
|
0x5000,0x5100,0x5200,0x5300,0x5400,0x5500,0x5600,0x5700,
|
|
0x5800,0x5900,0x6000,0x6100,0x6200,0x6300,0x6400,0x6500,
|
|
0x6000,0x6100,0x6200,0x6300,0x6400,0x6500,0x6600,0x6700,
|
|
0x6800,0x6900,0x7000,0x7100,0x7200,0x7300,0x7400,0x7500,
|
|
0x7000,0x7100,0x7200,0x7300,0x7400,0x7500,0x7600,0x7700,
|
|
0x7800,0x7900,0x8000,0x8100,0x8200,0x8300,0x8400,0x8500,
|
|
0x8000,0x8100,0x8200,0x8300,0x8400,0x8500,0x8600,0x8700,
|
|
0x8800,0x8900,0x9000,0x9100,0x9200,0x9300,0x9400,0x9500,
|
|
0x9000,0x9100,0x9200,0x9300,0x9400,0x9500,0x9600,0x9700,
|
|
0x9800,0x9900,0x0090,0x0110,0x0210,0x0310,0x0410,0x0510,
|
|
0x0090,0x0110,0x0210,0x0310,0x0410,0x0510,0x0610,0x0710,
|
|
0x0810,0x0910,0x1010,0x1110,0x1210,0x1310,0x1410,0x1510,
|
|
0x1010,0x1110,0x1210,0x1310,0x1410,0x1510,0x1610,0x1710,
|
|
0x1810,0x1910,0x2010,0x2110,0x2210,0x2310,0x2410,0x2510,
|
|
0x2010,0x2110,0x2210,0x2310,0x2410,0x2510,0x2610,0x2710,
|
|
0x2810,0x2910,0x3010,0x3110,0x3210,0x3310,0x3410,0x3510,
|
|
0x3010,0x3110,0x3210,0x3310,0x3410,0x3510,0x3610,0x3710,
|
|
0x3810,0x3910,0x4010,0x4110,0x4210,0x4310,0x4410,0x4510,
|
|
0x4010,0x4110,0x4210,0x4310,0x4410,0x4510,0x4610,0x4710,
|
|
0x4810,0x4910,0x5010,0x5110,0x5210,0x5310,0x5410,0x5510,
|
|
0x5010,0x5110,0x5210,0x5310,0x5410,0x5510,0x5610,0x5710,
|
|
0x5810,0x5910,0x6010,0x6110,0x6210,0x6310,0x6410,0x6510,
|
|
0x6010,0x6110,0x6210,0x6310,0x6410,0x6510,0x6610,0x6710,
|
|
0x6810,0x6910,0x7010,0x7110,0x7210,0x7310,0x7410,0x7510,
|
|
0x7010,0x7110,0x7210,0x7310,0x7410,0x7510,0x7610,0x7710,
|
|
0x7810,0x7910,0x8010,0x8110,0x8210,0x8310,0x8410,0x8510,
|
|
0x8010,0x8110,0x8210,0x8310,0x8410,0x8510,0x8610,0x8710,
|
|
0x8810,0x8910,0x9010,0x9110,0x9210,0x9310,0x9410,0x9510,
|
|
0x9010,0x9110,0x9210,0x9310,0x9410,0x9510,0x9610,0x9710,
|
|
0x9810,0x9910,0xA010,0xA110,0xA210,0xA310,0xA410,0xA510,
|
|
0xA010,0xA110,0xA210,0xA310,0xA410,0xA510,0xA610,0xA710,
|
|
0xA810,0xA910,0xB010,0xB110,0xB210,0xB310,0xB410,0xB510,
|
|
0xB010,0xB110,0xB210,0xB310,0xB410,0xB510,0xB610,0xB710,
|
|
0xB810,0xB910,0xC010,0xC110,0xC210,0xC310,0xC410,0xC510,
|
|
0xC010,0xC110,0xC210,0xC310,0xC410,0xC510,0xC610,0xC710,
|
|
0xC810,0xC910,0xD010,0xD110,0xD210,0xD310,0xD410,0xD510,
|
|
0xD010,0xD110,0xD210,0xD310,0xD410,0xD510,0xD610,0xD710,
|
|
0xD810,0xD910,0xE010,0xE110,0xE210,0xE310,0xE410,0xE510,
|
|
0xE010,0xE110,0xE210,0xE310,0xE410,0xE510,0xE610,0xE710,
|
|
0xE810,0xE910,0xF010,0xF110,0xF210,0xF310,0xF410,0xF510,
|
|
0xF010,0xF110,0xF210,0xF310,0xF410,0xF510,0xF610,0xF710,
|
|
0xF810,0xF910,0x0090,0x0110,0x0210,0x0310,0x0410,0x0510,
|
|
0x0090,0x0110,0x0210,0x0310,0x0410,0x0510,0x0610,0x0710,
|
|
0x0810,0x0910,0x1010,0x1110,0x1210,0x1310,0x1410,0x1510,
|
|
0x1010,0x1110,0x1210,0x1310,0x1410,0x1510,0x1610,0x1710,
|
|
0x1810,0x1910,0x2010,0x2110,0x2210,0x2310,0x2410,0x2510,
|
|
0x2010,0x2110,0x2210,0x2310,0x2410,0x2510,0x2610,0x2710,
|
|
0x2810,0x2910,0x3010,0x3110,0x3210,0x3310,0x3410,0x3510,
|
|
0x3010,0x3110,0x3210,0x3310,0x3410,0x3510,0x3610,0x3710,
|
|
0x3810,0x3910,0x4010,0x4110,0x4210,0x4310,0x4410,0x4510,
|
|
0x4010,0x4110,0x4210,0x4310,0x4410,0x4510,0x4610,0x4710,
|
|
0x4810,0x4910,0x5010,0x5110,0x5210,0x5310,0x5410,0x5510,
|
|
0x5010,0x5110,0x5210,0x5310,0x5410,0x5510,0x5610,0x5710,
|
|
0x5810,0x5910,0x6010,0x6110,0x6210,0x6310,0x6410,0x6510,
|
|
0x0600,0x0700,0x0800,0x0900,0x0A00,0x0B00,0x0C00,0x0D00,
|
|
0x0E00,0x0F00,0x1000,0x1100,0x1200,0x1300,0x1400,0x1500,
|
|
0x1600,0x1700,0x1800,0x1900,0x1A00,0x1B00,0x1C00,0x1D00,
|
|
0x1E00,0x1F00,0x2000,0x2100,0x2200,0x2300,0x2400,0x2500,
|
|
0x2600,0x2700,0x2800,0x2900,0x2A00,0x2B00,0x2C00,0x2D00,
|
|
0x2E00,0x2F00,0x3000,0x3100,0x3200,0x3300,0x3400,0x3500,
|
|
0x3600,0x3700,0x3800,0x3900,0x3A00,0x3B00,0x3C00,0x3D00,
|
|
0x3E00,0x3F00,0x4000,0x4100,0x4200,0x4300,0x4400,0x4500,
|
|
0x4600,0x4700,0x4800,0x4900,0x4A00,0x4B00,0x4C00,0x4D00,
|
|
0x4E00,0x4F00,0x5000,0x5100,0x5200,0x5300,0x5400,0x5500,
|
|
0x5600,0x5700,0x5800,0x5900,0x5A00,0x5B00,0x5C00,0x5D00,
|
|
0x5E00,0x5F00,0x6000,0x6100,0x6200,0x6300,0x6400,0x6500,
|
|
0x6600,0x6700,0x6800,0x6900,0x6A00,0x6B00,0x6C00,0x6D00,
|
|
0x6E00,0x6F00,0x7000,0x7100,0x7200,0x7300,0x7400,0x7500,
|
|
0x7600,0x7700,0x7800,0x7900,0x7A00,0x7B00,0x7C00,0x7D00,
|
|
0x7E00,0x7F00,0x8000,0x8100,0x8200,0x8300,0x8400,0x8500,
|
|
0x8600,0x8700,0x8800,0x8900,0x8A00,0x8B00,0x8C00,0x8D00,
|
|
0x8E00,0x8F00,0x9000,0x9100,0x9200,0x9300,0x9400,0x9500,
|
|
0x9600,0x9700,0x9800,0x9900,0x9A00,0x9B00,0x9C00,0x9D00,
|
|
0x9E00,0x9F00,0x0090,0x0110,0x0210,0x0310,0x0410,0x0510,
|
|
0x0610,0x0710,0x0810,0x0910,0x0A10,0x0B10,0x0C10,0x0D10,
|
|
0x0E10,0x0F10,0x1010,0x1110,0x1210,0x1310,0x1410,0x1510,
|
|
0x1610,0x1710,0x1810,0x1910,0x1A10,0x1B10,0x1C10,0x1D10,
|
|
0x1E10,0x1F10,0x2010,0x2110,0x2210,0x2310,0x2410,0x2510,
|
|
0x2610,0x2710,0x2810,0x2910,0x2A10,0x2B10,0x2C10,0x2D10,
|
|
0x2E10,0x2F10,0x3010,0x3110,0x3210,0x3310,0x3410,0x3510,
|
|
0x3610,0x3710,0x3810,0x3910,0x3A10,0x3B10,0x3C10,0x3D10,
|
|
0x3E10,0x3F10,0x4010,0x4110,0x4210,0x4310,0x4410,0x4510,
|
|
0x4610,0x4710,0x4810,0x4910,0x4A10,0x4B10,0x4C10,0x4D10,
|
|
0x4E10,0x4F10,0x5010,0x5110,0x5210,0x5310,0x5410,0x5510,
|
|
0x5610,0x5710,0x5810,0x5910,0x5A10,0x5B10,0x5C10,0x5D10,
|
|
0x5E10,0x5F10,0x6010,0x6110,0x6210,0x6310,0x6410,0x6510,
|
|
0x6610,0x6710,0x6810,0x6910,0x6A10,0x6B10,0x6C10,0x6D10,
|
|
0x6E10,0x6F10,0x7010,0x7110,0x7210,0x7310,0x7410,0x7510,
|
|
0x7610,0x7710,0x7810,0x7910,0x7A10,0x7B10,0x7C10,0x7D10,
|
|
0x7E10,0x7F10,0x8010,0x8110,0x8210,0x8310,0x8410,0x8510,
|
|
0x8610,0x8710,0x8810,0x8910,0x8A10,0x8B10,0x8C10,0x8D10,
|
|
0x8E10,0x8F10,0x9010,0x9110,0x9210,0x9310,0x9410,0x9510,
|
|
0x9610,0x9710,0x9810,0x9910,0x9A10,0x9B10,0x9C10,0x9D10,
|
|
0x9E10,0x9F10,0xA010,0xA110,0xA210,0xA310,0xA410,0xA510,
|
|
0xA610,0xA710,0xA810,0xA910,0xAA10,0xAB10,0xAC10,0xAD10,
|
|
0xAE10,0xAF10,0xB010,0xB110,0xB210,0xB310,0xB410,0xB510,
|
|
0xB610,0xB710,0xB810,0xB910,0xBA10,0xBB10,0xBC10,0xBD10,
|
|
0xBE10,0xBF10,0xC010,0xC110,0xC210,0xC310,0xC410,0xC510,
|
|
0xC610,0xC710,0xC810,0xC910,0xCA10,0xCB10,0xCC10,0xCD10,
|
|
0xCE10,0xCF10,0xD010,0xD110,0xD210,0xD310,0xD410,0xD510,
|
|
0xD610,0xD710,0xD810,0xD910,0xDA10,0xDB10,0xDC10,0xDD10,
|
|
0xDE10,0xDF10,0xE010,0xE110,0xE210,0xE310,0xE410,0xE510,
|
|
0xE610,0xE710,0xE810,0xE910,0xEA10,0xEB10,0xEC10,0xED10,
|
|
0xEE10,0xEF10,0xF010,0xF110,0xF210,0xF310,0xF410,0xF510,
|
|
0xF610,0xF710,0xF810,0xF910,0xFA10,0xFB10,0xFC10,0xFD10,
|
|
0xFE10,0xFF10,0x0090,0x0110,0x0210,0x0310,0x0410,0x0510,
|
|
0x0610,0x0710,0x0810,0x0910,0x0A10,0x0B10,0x0C10,0x0D10,
|
|
0x0E10,0x0F10,0x1010,0x1110,0x1210,0x1310,0x1410,0x1510,
|
|
0x1610,0x1710,0x1810,0x1910,0x1A10,0x1B10,0x1C10,0x1D10,
|
|
0x1E10,0x1F10,0x2010,0x2110,0x2210,0x2310,0x2410,0x2510,
|
|
0x2610,0x2710,0x2810,0x2910,0x2A10,0x2B10,0x2C10,0x2D10,
|
|
0x2E10,0x2F10,0x3010,0x3110,0x3210,0x3310,0x3410,0x3510,
|
|
0x3610,0x3710,0x3810,0x3910,0x3A10,0x3B10,0x3C10,0x3D10,
|
|
0x3E10,0x3F10,0x4010,0x4110,0x4210,0x4310,0x4410,0x4510,
|
|
0x4610,0x4710,0x4810,0x4910,0x4A10,0x4B10,0x4C10,0x4D10,
|
|
0x4E10,0x4F10,0x5010,0x5110,0x5210,0x5310,0x5410,0x5510,
|
|
0x5610,0x5710,0x5810,0x5910,0x5A10,0x5B10,0x5C10,0x5D10,
|
|
0x5E10,0x5F10,0x6010,0x6110,0x6210,0x6310,0x6410,0x6510,
|
|
0x00C0,0x0140,0x0240,0x0340,0x0440,0x0540,0x0640,0x0740,
|
|
0x0840,0x0940,0x0A40,0x0B40,0x0C40,0x0D40,0x0E40,0x0F40,
|
|
0x1040,0x1140,0x1240,0x1340,0x1440,0x1540,0x1640,0x1740,
|
|
0x1840,0x1940,0x1A40,0x1B40,0x1C40,0x1D40,0x1E40,0x1F40,
|
|
0x2040,0x2140,0x2240,0x2340,0x2440,0x2540,0x2640,0x2740,
|
|
0x2840,0x2940,0x2A40,0x2B40,0x2C40,0x2D40,0x2E40,0x2F40,
|
|
0x3040,0x3140,0x3240,0x3340,0x3440,0x3540,0x3640,0x3740,
|
|
0x3840,0x3940,0x3A40,0x3B40,0x3C40,0x3D40,0x3E40,0x3F40,
|
|
0x4040,0x4140,0x4240,0x4340,0x4440,0x4540,0x4640,0x4740,
|
|
0x4840,0x4940,0x4A40,0x4B40,0x4C40,0x4D40,0x4E40,0x4F40,
|
|
0x5040,0x5140,0x5240,0x5340,0x5440,0x5540,0x5640,0x5740,
|
|
0x5840,0x5940,0x5A40,0x5B40,0x5C40,0x5D40,0x5E40,0x5F40,
|
|
0x6040,0x6140,0x6240,0x6340,0x6440,0x6540,0x6640,0x6740,
|
|
0x6840,0x6940,0x6A40,0x6B40,0x6C40,0x6D40,0x6E40,0x6F40,
|
|
0x7040,0x7140,0x7240,0x7340,0x7440,0x7540,0x7640,0x7740,
|
|
0x7840,0x7940,0x7A40,0x7B40,0x7C40,0x7D40,0x7E40,0x7F40,
|
|
0x8040,0x8140,0x8240,0x8340,0x8440,0x8540,0x8640,0x8740,
|
|
0x8840,0x8940,0x8A40,0x8B40,0x8C40,0x8D40,0x8E40,0x8F40,
|
|
0x9040,0x9140,0x9240,0x9340,0x9440,0x9540,0x9640,0x9740,
|
|
0x9840,0x9940,0x9A40,0x9B40,0x9C40,0x9D40,0x9E40,0x9F40,
|
|
0xA040,0xA140,0xA240,0xA340,0xA440,0xA540,0xA640,0xA740,
|
|
0xA840,0xA940,0xAA40,0xAB40,0xAC40,0xAD40,0xAE40,0xAF40,
|
|
0xB040,0xB140,0xB240,0xB340,0xB440,0xB540,0xB640,0xB740,
|
|
0xB840,0xB940,0xBA40,0xBB40,0xBC40,0xBD40,0xBE40,0xBF40,
|
|
0xC040,0xC140,0xC240,0xC340,0xC440,0xC540,0xC640,0xC740,
|
|
0xC840,0xC940,0xCA40,0xCB40,0xCC40,0xCD40,0xCE40,0xCF40,
|
|
0xD040,0xD140,0xD240,0xD340,0xD440,0xD540,0xD640,0xD740,
|
|
0xD840,0xD940,0xDA40,0xDB40,0xDC40,0xDD40,0xDE40,0xDF40,
|
|
0xE040,0xE140,0xE240,0xE340,0xE440,0xE540,0xE640,0xE740,
|
|
0xE840,0xE940,0xEA40,0xEB40,0xEC40,0xED40,0xEE40,0xEF40,
|
|
0xF040,0xF140,0xF240,0xF340,0xF440,0xF540,0xF640,0xF740,
|
|
0xF840,0xF940,0xFA40,0xFB40,0xFC40,0xFD40,0xFE40,0xFF40,
|
|
0xA050,0xA150,0xA250,0xA350,0xA450,0xA550,0xA650,0xA750,
|
|
0xA850,0xA950,0xAA50,0xAB50,0xAC50,0xAD50,0xAE50,0xAF50,
|
|
0xB050,0xB150,0xB250,0xB350,0xB450,0xB550,0xB650,0xB750,
|
|
0xB850,0xB950,0xBA50,0xBB50,0xBC50,0xBD50,0xBE50,0xBF50,
|
|
0xC050,0xC150,0xC250,0xC350,0xC450,0xC550,0xC650,0xC750,
|
|
0xC850,0xC950,0xCA50,0xCB50,0xCC50,0xCD50,0xCE50,0xCF50,
|
|
0xD050,0xD150,0xD250,0xD350,0xD450,0xD550,0xD650,0xD750,
|
|
0xD850,0xD950,0xDA50,0xDB50,0xDC50,0xDD50,0xDE50,0xDF50,
|
|
0xE050,0xE150,0xE250,0xE350,0xE450,0xE550,0xE650,0xE750,
|
|
0xE850,0xE950,0xEA50,0xEB50,0xEC50,0xED50,0xEE50,0xEF50,
|
|
0xF050,0xF150,0xF250,0xF350,0xF450,0xF550,0xF650,0xF750,
|
|
0xF850,0xF950,0xFA50,0xFB50,0xFC50,0xFD50,0xFE50,0xFF50,
|
|
0x00D0,0x0150,0x0250,0x0350,0x0450,0x0550,0x0650,0x0750,
|
|
0x0850,0x0950,0x0A50,0x0B50,0x0C50,0x0D50,0x0E50,0x0F50,
|
|
0x1050,0x1150,0x1250,0x1350,0x1450,0x1550,0x1650,0x1750,
|
|
0x1850,0x1950,0x1A50,0x1B50,0x1C50,0x1D50,0x1E50,0x1F50,
|
|
0x2050,0x2150,0x2250,0x2350,0x2450,0x2550,0x2650,0x2750,
|
|
0x2850,0x2950,0x2A50,0x2B50,0x2C50,0x2D50,0x2E50,0x2F50,
|
|
0x3050,0x3150,0x3250,0x3350,0x3450,0x3550,0x3650,0x3750,
|
|
0x3850,0x3950,0x3A50,0x3B50,0x3C50,0x3D50,0x3E50,0x3F50,
|
|
0x4050,0x4150,0x4250,0x4350,0x4450,0x4550,0x4650,0x4750,
|
|
0x4850,0x4950,0x4A50,0x4B50,0x4C50,0x4D50,0x4E50,0x4F50,
|
|
0x5050,0x5150,0x5250,0x5350,0x5450,0x5550,0x5650,0x5750,
|
|
0x5850,0x5950,0x5A50,0x5B50,0x5C50,0x5D50,0x5E50,0x5F50,
|
|
0x6050,0x6150,0x6250,0x6350,0x6450,0x6550,0x6650,0x6750,
|
|
0x6850,0x6950,0x6A50,0x6B50,0x6C50,0x6D50,0x6E50,0x6F50,
|
|
0x7050,0x7150,0x7250,0x7350,0x7450,0x7550,0x7650,0x7750,
|
|
0x7850,0x7950,0x7A50,0x7B50,0x7C50,0x7D50,0x7E50,0x7F50,
|
|
0x8050,0x8150,0x8250,0x8350,0x8450,0x8550,0x8650,0x8750,
|
|
0x8850,0x8950,0x8A50,0x8B50,0x8C50,0x8D50,0x8E50,0x8F50,
|
|
0x9050,0x9150,0x9250,0x9350,0x9450,0x9550,0x9650,0x9750,
|
|
0x9850,0x9950,0x9A50,0x9B50,0x9C50,0x9D50,0x9E50,0x9F50,
|
|
0xFA40,0xFB40,0xFC40,0xFD40,0xFE40,0xFF40,0x00C0,0x0140,
|
|
0x0240,0x0340,0x0440,0x0540,0x0640,0x0740,0x0840,0x0940,
|
|
0x0A40,0x0B40,0x0C40,0x0D40,0x0E40,0x0F40,0x1040,0x1140,
|
|
0x1240,0x1340,0x1440,0x1540,0x1640,0x1740,0x1840,0x1940,
|
|
0x1A40,0x1B40,0x1C40,0x1D40,0x1E40,0x1F40,0x2040,0x2140,
|
|
0x2240,0x2340,0x2440,0x2540,0x2640,0x2740,0x2840,0x2940,
|
|
0x2A40,0x2B40,0x2C40,0x2D40,0x2E40,0x2F40,0x3040,0x3140,
|
|
0x3240,0x3340,0x3440,0x3540,0x3640,0x3740,0x3840,0x3940,
|
|
0x3A40,0x3B40,0x3C40,0x3D40,0x3E40,0x3F40,0x4040,0x4140,
|
|
0x4240,0x4340,0x4440,0x4540,0x4640,0x4740,0x4840,0x4940,
|
|
0x4A40,0x4B40,0x4C40,0x4D40,0x4E40,0x4F40,0x5040,0x5140,
|
|
0x5240,0x5340,0x5440,0x5540,0x5640,0x5740,0x5840,0x5940,
|
|
0x5A40,0x5B40,0x5C40,0x5D40,0x5E40,0x5F40,0x6040,0x6140,
|
|
0x6240,0x6340,0x6440,0x6540,0x6640,0x6740,0x6840,0x6940,
|
|
0x6A40,0x6B40,0x6C40,0x6D40,0x6E40,0x6F40,0x7040,0x7140,
|
|
0x7240,0x7340,0x7440,0x7540,0x7640,0x7740,0x7840,0x7940,
|
|
0x7A40,0x7B40,0x7C40,0x7D40,0x7E40,0x7F40,0x8040,0x8140,
|
|
0x8240,0x8340,0x8440,0x8540,0x8640,0x8740,0x8840,0x8940,
|
|
0x8A40,0x8B40,0x8C40,0x8D40,0x8E40,0x8F40,0x9040,0x9140,
|
|
0x9240,0x9340,0x9440,0x9540,0x9640,0x9740,0x9840,0x9940,
|
|
0x9A40,0x9B40,0x9C40,0x9D40,0x9E40,0x9F40,0xA040,0xA140,
|
|
0xA240,0xA340,0xA440,0xA540,0xA640,0xA740,0xA840,0xA940,
|
|
0xAA40,0xAB40,0xAC40,0xAD40,0xAE40,0xAF40,0xB040,0xB140,
|
|
0xB240,0xB340,0xB440,0xB540,0xB640,0xB740,0xB840,0xB940,
|
|
0xBA40,0xBB40,0xBC40,0xBD40,0xBE40,0xBF40,0xC040,0xC140,
|
|
0xC240,0xC340,0xC440,0xC540,0xC640,0xC740,0xC840,0xC940,
|
|
0xCA40,0xCB40,0xCC40,0xCD40,0xCE40,0xCF40,0xD040,0xD140,
|
|
0xD240,0xD340,0xD440,0xD540,0xD640,0xD740,0xD840,0xD940,
|
|
0xDA40,0xDB40,0xDC40,0xDD40,0xDE40,0xDF40,0xE040,0xE140,
|
|
0xE240,0xE340,0xE440,0xE540,0xE640,0xE740,0xE840,0xE940,
|
|
0xEA40,0xEB40,0xEC40,0xED40,0xEE40,0xEF40,0xF040,0xF140,
|
|
0xF240,0xF340,0xF440,0xF540,0xF640,0xF740,0xF840,0xF940,
|
|
0x9A50,0x9B50,0x9C50,0x9D50,0x9E50,0x9F50,0xA050,0xA150,
|
|
0xA250,0xA350,0xA450,0xA550,0xA650,0xA750,0xA850,0xA950,
|
|
0xAA50,0xAB50,0xAC50,0xAD50,0xAE50,0xAF50,0xB050,0xB150,
|
|
0xB250,0xB350,0xB450,0xB550,0xB650,0xB750,0xB850,0xB950,
|
|
0xBA50,0xBB50,0xBC50,0xBD50,0xBE50,0xBF50,0xC050,0xC150,
|
|
0xC250,0xC350,0xC450,0xC550,0xC650,0xC750,0xC850,0xC950,
|
|
0xCA50,0xCB50,0xCC50,0xCD50,0xCE50,0xCF50,0xD050,0xD150,
|
|
0xD250,0xD350,0xD450,0xD550,0xD650,0xD750,0xD850,0xD950,
|
|
0xDA50,0xDB50,0xDC50,0xDD50,0xDE50,0xDF50,0xE050,0xE150,
|
|
0xE250,0xE350,0xE450,0xE550,0xE650,0xE750,0xE850,0xE950,
|
|
0xEA50,0xEB50,0xEC50,0xED50,0xEE50,0xEF50,0xF050,0xF150,
|
|
0xF250,0xF350,0xF450,0xF550,0xF650,0xF750,0xF850,0xF950,
|
|
0xFA50,0xFB50,0xFC50,0xFD50,0xFE50,0xFF50,0x00D0,0x0150,
|
|
0x0250,0x0350,0x0450,0x0550,0x0650,0x0750,0x0850,0x0950,
|
|
0x0A50,0x0B50,0x0C50,0x0D50,0x0E50,0x0F50,0x1050,0x1150,
|
|
0x1250,0x1350,0x1450,0x1550,0x1650,0x1750,0x1850,0x1950,
|
|
0x1A50,0x1B50,0x1C50,0x1D50,0x1E50,0x1F50,0x2050,0x2150,
|
|
0x2250,0x2350,0x2450,0x2550,0x2650,0x2750,0x2850,0x2950,
|
|
0x2A50,0x2B50,0x2C50,0x2D50,0x2E50,0x2F50,0x3050,0x3150,
|
|
0x3250,0x3350,0x3450,0x3550,0x3650,0x3750,0x3850,0x3950,
|
|
0x3A50,0x3B50,0x3C50,0x3D50,0x3E50,0x3F50,0x4050,0x4150,
|
|
0x4250,0x4350,0x4450,0x4550,0x4650,0x4750,0x4850,0x4950,
|
|
0x4A50,0x4B50,0x4C50,0x4D50,0x4E50,0x4F50,0x5050,0x5150,
|
|
0x5250,0x5350,0x5450,0x5550,0x5650,0x5750,0x5850,0x5950,
|
|
0x5A50,0x5B50,0x5C50,0x5D50,0x5E50,0x5F50,0x6050,0x6150,
|
|
0x6250,0x6350,0x6450,0x6550,0x6650,0x6750,0x6850,0x6950,
|
|
0x6A50,0x6B50,0x6C50,0x6D50,0x6E50,0x6F50,0x7050,0x7150,
|
|
0x7250,0x7350,0x7450,0x7550,0x7650,0x7750,0x7850,0x7950,
|
|
0x7A50,0x7B50,0x7C50,0x7D50,0x7E50,0x7F50,0x8050,0x8150,
|
|
0x8250,0x8350,0x8450,0x8550,0x8650,0x8750,0x8850,0x8950,
|
|
0x8A50,0x8B50,0x8C50,0x8D50,0x8E50,0x8F50,0x9050,0x9150,
|
|
0x9250,0x9350,0x9450,0x9550,0x9650,0x9750,0x9850,0x9950,
|
|
};
|
|
|
|
u8 ZeroTable[256] = {
|
|
0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0
|
|
};
|
|
|
|
#define GBSAVE_GAME_VERSION_1 1
|
|
#define GBSAVE_GAME_VERSION_2 2
|
|
#define GBSAVE_GAME_VERSION_3 3
|
|
#define GBSAVE_GAME_VERSION_4 4
|
|
#define GBSAVE_GAME_VERSION_5 5
|
|
#define GBSAVE_GAME_VERSION_6 6
|
|
#define GBSAVE_GAME_VERSION_7 7
|
|
#define GBSAVE_GAME_VERSION_8 8
|
|
#define GBSAVE_GAME_VERSION_9 9
|
|
#define GBSAVE_GAME_VERSION_10 10
|
|
#define GBSAVE_GAME_VERSION_11 11
|
|
#define GBSAVE_GAME_VERSION_12 12
|
|
#define GBSAVE_GAME_VERSION GBSAVE_GAME_VERSION_12
|
|
|
|
int inline gbGetValue(int min,int max,int v)
|
|
{
|
|
return (int)(min+(float)(max-min)*(2.0*(v/31.0)-(v/31.0)*(v/31.0)));
|
|
}
|
|
|
|
void gbGenFilter()
|
|
{
|
|
for (int r=0;r<32;r++) {
|
|
for (int g=0;g<32;g++) {
|
|
for (int b=0;b<32;b++) {
|
|
int nr=gbGetValue(gbGetValue(4,14,g),
|
|
gbGetValue(24,29,g),r)-4;
|
|
int ng=gbGetValue(gbGetValue(4+gbGetValue(0,5,r),
|
|
14+gbGetValue(0,3,r),b),
|
|
gbGetValue(24+gbGetValue(0,3,r),
|
|
29+gbGetValue(0,1,r),b),g)-4;
|
|
int nb=gbGetValue(gbGetValue(4+gbGetValue(0,5,r),
|
|
14+gbGetValue(0,3,r),g),
|
|
gbGetValue(24+gbGetValue(0,3,r),
|
|
29+gbGetValue(0,1,r),g),b)-4;
|
|
gbColorFilter[(b<<10)|(g<<5)|r]=(nb<<10)|(ng<<5)|nr;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool gbIsGameboyRom(char * file)
|
|
{
|
|
if(strlen(file) > 4) {
|
|
char * p = strrchr(file,'.');
|
|
|
|
if(p != NULL) {
|
|
if(_stricmp(p, ".gb") == 0)
|
|
return true;
|
|
if(_stricmp(p, ".dmg") == 0)
|
|
return true;
|
|
if(_stricmp(p, ".gbc") == 0)
|
|
return true;
|
|
if(_stricmp(p, ".cgb") == 0)
|
|
return true;
|
|
if(_stricmp(p, ".sgb") == 0)
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void gbCopyMemory(u16 d, u16 s, int count)
|
|
{
|
|
while(count) {
|
|
gbMemoryMap[d>>12][d & 0x0fff] = gbMemoryMap[s>>12][s & 0x0fff];
|
|
s++;
|
|
d++;
|
|
count--;
|
|
}
|
|
}
|
|
|
|
void gbDoHdma()
|
|
{
|
|
|
|
gbCopyMemory((gbHdmaDestination & 0x1ff0) | 0x8000,
|
|
gbHdmaSource & 0xfff0,
|
|
0x10);
|
|
|
|
gbHdmaDestination += 0x10;
|
|
if (gbHdmaDestination == 0xa000)
|
|
gbHdmaDestination = 0x8000;
|
|
|
|
gbHdmaSource += 0x10;
|
|
if (gbHdmaSource == 0x8000)
|
|
gbHdmaSource = 0xa000;
|
|
|
|
register_HDMA2 = gbHdmaSource & 0xff;
|
|
register_HDMA1 = gbHdmaSource>>8;
|
|
|
|
register_HDMA4 = gbHdmaDestination & 0xff;
|
|
register_HDMA3 = gbHdmaDestination>>8;
|
|
|
|
gbHdmaBytes -= 0x10;
|
|
gbMemory[0xff55] = --register_HDMA5;
|
|
if(register_HDMA5 == 0xff)
|
|
gbHdmaOn = 0;
|
|
|
|
// We need to add the dmaClockticks for HDMA !
|
|
if(gbSpeed)
|
|
gbDmaTicks = 17;
|
|
else
|
|
gbDmaTicks = 9;
|
|
|
|
if (IFF & 0x80)
|
|
gbDmaTicks++;
|
|
|
|
}
|
|
|
|
// fix for Harley and Lego Racers
|
|
void gbCompareLYToLYC()
|
|
{
|
|
if(register_LCDC & 0x80) {
|
|
if(register_LY == register_LYC) {
|
|
|
|
// mark that we have a match
|
|
register_STAT |= 4;
|
|
|
|
// check if we need an interrupt
|
|
if (register_STAT & 0x40)
|
|
{
|
|
// send LCD interrupt only if no interrupt 48h signal...
|
|
if (!gbInt48Signal)
|
|
{
|
|
register_IF |=2;
|
|
}
|
|
gbInt48Signal |= 8;
|
|
}
|
|
}
|
|
else // no match
|
|
{
|
|
register_STAT &= 0xfb;
|
|
gbInt48Signal &=~8;
|
|
}
|
|
}
|
|
}
|
|
|
|
void gbWriteMemory(u16 address, u8 value)
|
|
{
|
|
|
|
if(address < 0x8000) {
|
|
#ifndef FINAL_VERSION
|
|
if(memorydebug && (address>0x3fff || address < 0x2000)) {
|
|
log("Memory register write %04x=%02x PC=%04x\n",
|
|
address,
|
|
value,
|
|
PC.W);
|
|
}
|
|
|
|
#endif
|
|
if(mapper)
|
|
(*mapper)(address, value);
|
|
return;
|
|
|
|
}
|
|
|
|
if(address < 0xa000) {
|
|
// No access to Vram during mode 3
|
|
// (used to emulate the gfx differences between GB & GBC-GBA/SP in Stunt Racer)
|
|
if ((gbLcdModeDelayed !=3) ||
|
|
// This part is used to emulate a small difference between hardwares
|
|
// (check 8-in-1's arrow on GBA/GBC to verify it)
|
|
((register_LY == 0) && ((gbHardware & 0xa) && (gbScreenOn==false) &&
|
|
(register_LCDC & 0x80)) &&
|
|
(gbLcdLYIncrementTicksDelayed ==(GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS))))
|
|
gbMemoryMap[address>>12][address&0x0fff] = value;
|
|
return;
|
|
}
|
|
|
|
// Used for the mirroring of 0xC000 in 0xE000
|
|
if ((address >= 0xe000) && (address < 0xfe00))
|
|
address &= ~0x2000;
|
|
|
|
if(address < 0xc000) {
|
|
#ifndef FINAL_VERSION
|
|
if(memorydebug) {
|
|
log("Memory register write %04x=%02x PC=%04x\n",
|
|
address,
|
|
value,
|
|
PC.W);
|
|
}
|
|
#endif
|
|
|
|
// Is that a correct fix ??? (it used to be 'if (mapper)')...
|
|
if(mapperRAM)
|
|
(*mapperRAM)(address, value);
|
|
return;
|
|
}
|
|
|
|
|
|
if(address < 0xfe00) {
|
|
gbMemoryMap[address>>12][address & 0x0fff] = value;
|
|
return;
|
|
}
|
|
|
|
// OAM not accessible during mode 2 & 3.
|
|
if(address < 0xfea0)
|
|
{
|
|
if (((gbHardware & 0xa) && ((gbLcdMode | gbLcdModeDelayed) &2)) ||
|
|
((gbHardware & 5) && (((gbLcdModeDelayed == 2) &&
|
|
(gbLcdTicksDelayed<=GBLCD_MODE_2_CLOCK_TICKS)) ||
|
|
(gbLcdModeDelayed == 3))))
|
|
return;
|
|
else
|
|
{
|
|
gbMemory[address] = value;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if((address > 0xfea0) && (address < 0xff00)){ // GBC allows reading/writing to that area
|
|
gbMemory[address] = value;
|
|
return;
|
|
}
|
|
|
|
switch(address & 0x00ff) {
|
|
|
|
case 0x00: {
|
|
gbMemory[0xff00] = ((gbMemory[0xff00] & 0xcf) |
|
|
(value & 0x30) | 0xc0);
|
|
if(gbSgbMode) {
|
|
gbSgbDoBitTransfer(value);
|
|
}
|
|
return;
|
|
}
|
|
|
|
case 0x01: {
|
|
gbMemory[0xff01] = value;
|
|
return;
|
|
}
|
|
|
|
// serial control
|
|
case 0x02: {
|
|
gbSerialOn = (value & 0x80);
|
|
gbMemory[0xff02] = value;
|
|
if(gbSerialOn) {
|
|
gbSerialTicks = GBSERIAL_CLOCK_TICKS;
|
|
#ifdef OLD_GB_LINK
|
|
if(linkConnected) {
|
|
if(value & 1) {
|
|
linkSendByte(0x100|gbMemory[0xFF01]);
|
|
Sleep(5);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
gbSerialBits = 0;
|
|
return;
|
|
}
|
|
|
|
case 0x04: {
|
|
// DIV register resets on any write
|
|
// (not totally perfect, but better than nothing)
|
|
gbMemory[0xff04] = register_DIV = 0;
|
|
gbDivTicks = GBDIV_CLOCK_TICKS;
|
|
// Another weird timer 'bug' :
|
|
// Writing to DIV register resets the internal timer,
|
|
// and can also increase TIMA/trigger an interrupt
|
|
// in some cases...
|
|
if (gbTimerOn && !(gbInternalTimer & (gbTimerClockTicks>>1)))
|
|
{
|
|
gbMemory[0xff05] = ++register_TIMA;
|
|
if(register_TIMA == 0) {
|
|
// timer overflow!
|
|
|
|
// reload timer modulo
|
|
gbMemory[0xff05] = register_TIMA = register_TMA;
|
|
|
|
// flag interrupt
|
|
gbMemory[0xff0f] = register_IF |= 4;
|
|
}
|
|
}
|
|
gbInternalTimer = 0xff;
|
|
return;
|
|
}
|
|
case 0x05:
|
|
gbMemory[0xff05] = register_TIMA = value;
|
|
return;
|
|
|
|
case 0x06:
|
|
gbMemory[0xff06] = register_TMA = value;
|
|
return;
|
|
|
|
// TIMER control
|
|
case 0x07: {
|
|
|
|
gbTimerModeChange = (((value & 3) != (register_TAC&3)) && (value & register_TAC & 4)) ? true : false;
|
|
gbTimerOnChange = (((value ^ register_TAC) & 4) == 4) ? true : false;
|
|
|
|
gbTimerOn = (value & 4) ? true : false;
|
|
|
|
if (gbTimerOnChange || gbTimerModeChange)
|
|
{
|
|
gbTimerMode = value & 3;
|
|
|
|
switch(gbTimerMode) {
|
|
case 0:
|
|
gbTimerClockTicks = GBTIMER_MODE_0_CLOCK_TICKS;
|
|
break;
|
|
case 1:
|
|
gbTimerClockTicks = GBTIMER_MODE_1_CLOCK_TICKS;
|
|
break;
|
|
case 2:
|
|
gbTimerClockTicks = GBTIMER_MODE_2_CLOCK_TICKS;
|
|
break;
|
|
case 3:
|
|
gbTimerClockTicks = GBTIMER_MODE_3_CLOCK_TICKS;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
// This looks weird, but this emulates a bug in which register_TIMA
|
|
// is increased when writing to register_TAC
|
|
// (This fixes Korodice's long-delay between menus bug).
|
|
|
|
if (gbTimerOnChange || gbTimerModeChange)
|
|
{
|
|
bool temp = false;
|
|
|
|
if ((gbTimerOn && !gbTimerModeChange) && (gbTimerMode & 2) &&
|
|
!(gbInternalTimer & 0x80) && (gbInternalTimer & (gbTimerClockTicks>>1)) &&
|
|
!(gbInternalTimer & (gbTimerClockTicks>>5)))
|
|
temp = true;
|
|
else if ((!gbTimerOn && !gbTimerModeChange && gbTimerOnChange ) && ((gbTimerTicks-1) < (gbTimerClockTicks>>1)))
|
|
temp = true;
|
|
else if (gbTimerOn && gbTimerModeChange && !gbTimerOnChange)
|
|
{
|
|
switch(gbTimerMode & 3)
|
|
{
|
|
case 0x00:
|
|
temp = false;
|
|
break;
|
|
case 0x01:
|
|
if (((gbInternalTimer & 0x82) == 2) && (gbTimerTicks>(clockTicks+1)))
|
|
temp = true;
|
|
break;
|
|
case 0x02:
|
|
if (((gbInternalTimer & 0x88) == 0x8) && (gbTimerTicks>(clockTicks+1)))
|
|
temp = true;
|
|
break;
|
|
case 0x03:
|
|
if (((gbInternalTimer & 0xA0) == 0x20) && (gbTimerTicks>(clockTicks+1)))
|
|
temp = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (temp)
|
|
{
|
|
gbMemory[0xff05] = ++register_TIMA;
|
|
if((register_TIMA & 0xff) == 0) {
|
|
// timer overflow!
|
|
|
|
// reload timer modulo
|
|
gbMemory[0xff05] = register_TIMA = register_TMA;
|
|
|
|
// flag interrupt
|
|
gbMemory[0xff0f] = register_IF |= 4;
|
|
}
|
|
}
|
|
}
|
|
gbMemory[0xff07] = register_TAC = value;
|
|
return;
|
|
}
|
|
|
|
case 0x0f: {
|
|
gbMemory[0xff0f] = register_IF = value;
|
|
//gbMemory[0xff0f] = register_IE |= value;
|
|
return;
|
|
}
|
|
|
|
case 0x10:
|
|
case 0x11:
|
|
case 0x12:
|
|
case 0x13:
|
|
case 0x14:
|
|
case 0x16:
|
|
case 0x17:
|
|
case 0x18:
|
|
case 0x19:
|
|
case 0x1a:
|
|
case 0x1b:
|
|
case 0x1c:
|
|
case 0x1d:
|
|
case 0x1e:
|
|
case 0x20:
|
|
case 0x21:
|
|
case 0x22:
|
|
case 0x23:
|
|
case 0x24:
|
|
case 0x25: {
|
|
if (gbMemory[NR52] & 0x80) {
|
|
SOUND_EVENT(address,value);
|
|
return;
|
|
}
|
|
}
|
|
|
|
case 0x26: {
|
|
SOUND_EVENT(address,value);
|
|
return;
|
|
}
|
|
|
|
case 0x30:
|
|
case 0x31:
|
|
case 0x32:
|
|
case 0x33:
|
|
case 0x34:
|
|
case 0x35:
|
|
case 0x36:
|
|
case 0x37:
|
|
case 0x38:
|
|
case 0x39:
|
|
case 0x3a:
|
|
case 0x3b:
|
|
case 0x3c:
|
|
case 0x3d:
|
|
case 0x3e:
|
|
case 0x3f: {
|
|
SOUND_EVENT(address,value);
|
|
//gbMemory[address] = value;
|
|
return;
|
|
}
|
|
|
|
case 0x40: {
|
|
int lcdChange = (register_LCDC & 0x80) ^ (value & 0x80);
|
|
|
|
// don't draw the window if it was not enabled and not being drawn before
|
|
if(!(register_LCDC & 0x20) && (value & 0x20) && gbWindowLine == -1 &&
|
|
register_LY > register_WY)
|
|
gbWindowLine = 146;
|
|
// 007 fix : don't draw the first window's 1st line if it's enable 'too late'
|
|
// (ie. if register_LY == register_WY when enabling it)
|
|
// and move it to the next line
|
|
else if (!(register_LCDC & 0x20) && (value & 0x20) && (register_LY == register_WY))
|
|
gbWindowLine = -2;
|
|
|
|
|
|
gbMemory[0xff40] = register_LCDC = value;
|
|
|
|
|
|
if(lcdChange) {
|
|
if((value & 0x80) && (!register_LCDCBusy)) {
|
|
|
|
// if (!gbWhiteScreen && !gbSgbMask)
|
|
|
|
// systemDrawScreen();
|
|
|
|
|
|
|
|
gbRegisterLYLCDCOffOn = (register_LY + 144) % 154;
|
|
|
|
gbLcdTicks = GBLCD_MODE_2_CLOCK_TICKS - (gbSpeed ? 2 : 1);
|
|
gbLcdTicksDelayed = GBLCD_MODE_2_CLOCK_TICKS - (gbSpeed ? 1 : 0);
|
|
gbLcdLYIncrementTicks = GBLY_INCREMENT_CLOCK_TICKS - (gbSpeed ? 2 : 1);
|
|
gbLcdLYIncrementTicksDelayed = GBLY_INCREMENT_CLOCK_TICKS - (gbSpeed ? 1 : 0);
|
|
gbLcdMode = 2;
|
|
gbLcdModeDelayed = 2;
|
|
gbMemory[0xff41] = register_STAT = (register_STAT & 0xfc) | 2;
|
|
gbMemory[0xff44] = register_LY = 0x00;
|
|
gbInt48Signal = 0;
|
|
gbLYChangeHappened = false;
|
|
gbLCDChangeHappened = false;
|
|
gbWindowLine = 146;
|
|
oldRegister_WY = 146;
|
|
|
|
// Fix for Namco Gallery Vol.2
|
|
// (along with updating register_LCDC at the start of 'case 0x40') :
|
|
if(register_STAT & 0x20)
|
|
{
|
|
// send LCD interrupt only if no interrupt 48h signal...
|
|
if (!gbInt48Signal)
|
|
{
|
|
gbMemory[0xff0f] = register_IF |= 2;
|
|
}
|
|
gbInt48Signal |= 4;
|
|
}
|
|
gbCompareLYToLYC();
|
|
|
|
} else {
|
|
|
|
register_LCDCBusy = clockTicks+6;
|
|
|
|
//used to update the screen with white lines when it's off.
|
|
//(it looks strange, but it's pretty accurate)
|
|
|
|
gbWhiteScreen = 0;
|
|
|
|
gbScreenTicks = ((150-register_LY)*GBLY_INCREMENT_CLOCK_TICKS +
|
|
(49<<(gbSpeed ? 1 : 0)));
|
|
|
|
// disable the screen rendering
|
|
gbScreenOn = false;
|
|
gbLcdTicks = 0;
|
|
gbLcdMode = 0;
|
|
gbLcdModeDelayed = 0;
|
|
gbMemory[0xff41] = register_STAT &= 0xfc;
|
|
gbInt48Signal = 0;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
// STAT
|
|
case 0x41: {
|
|
//register_STAT = (register_STAT & 0x87) |
|
|
// (value & 0x7c);
|
|
gbMemory[0xff41] = register_STAT = (value & 0xf8) | (register_STAT & 0x07); // fix ?
|
|
// GB bug from Devrs FAQ
|
|
// proper fix
|
|
gbInt48Signal &= ((register_STAT>>3) & 0xF);
|
|
|
|
if((register_LCDC & 0x80)) {
|
|
if ((register_STAT & 0x08) && (gbLcdMode == 0))
|
|
{
|
|
if (!gbInt48Signal)
|
|
{
|
|
gbMemory[0xff0f] = register_IF |=2;
|
|
}
|
|
gbInt48Signal |= 1;
|
|
}
|
|
if ((register_STAT & 0x10) && (gbLcdMode == 1))
|
|
{
|
|
if (!gbInt48Signal)
|
|
{
|
|
gbMemory[0xff0f] = register_IF |=2;
|
|
}
|
|
gbInt48Signal |= 2;
|
|
}
|
|
if ((register_STAT & 0x20) && (gbLcdMode == 2))
|
|
{
|
|
if (!gbInt48Signal)
|
|
{
|
|
gbMemory[0xff0f] = register_IF |=2;
|
|
}
|
|
gbInt48Signal |= 4;
|
|
}
|
|
gbCompareLYToLYC();
|
|
|
|
gbMemory[0xff0f] = register_IF;
|
|
gbMemory[0xff41] = register_STAT;
|
|
|
|
}
|
|
return;
|
|
}
|
|
|
|
// SCY
|
|
case 0x42: {
|
|
int temp = -1;
|
|
|
|
if ((gbLcdMode == 3) || (gbLcdModeDelayed == 3))
|
|
temp = ((GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS) -
|
|
gbLcdLYIncrementTicks);
|
|
|
|
if (temp >=0)
|
|
{
|
|
for (int i=temp<<(gbSpeed ? 1 : 2);i<300;i++)
|
|
if (temp < 300)
|
|
gbSCYLine[i] = value;
|
|
}
|
|
|
|
else
|
|
memset(gbSCYLine, value, sizeof(gbSCYLine));
|
|
|
|
gbMemory[0xff42] = register_SCY = value;
|
|
return;
|
|
}
|
|
|
|
// SCX
|
|
case 0x43: {
|
|
int temp = -1;
|
|
|
|
if (gbLcdModeDelayed == 3)
|
|
temp = ((GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS) -
|
|
gbLcdLYIncrementTicksDelayed);
|
|
|
|
if (temp >=0)
|
|
{
|
|
for (int i=temp<<(gbSpeed ? 1 : 2);i<300;i++)
|
|
if (temp < 300)
|
|
gbSCXLine[i] = value;
|
|
}
|
|
|
|
else
|
|
memset(gbSCXLine, value, sizeof(gbSCXLine));
|
|
|
|
gbMemory[0xff43] = register_SCX = value;
|
|
return;
|
|
}
|
|
|
|
// LY
|
|
case 0x44: {
|
|
// read only
|
|
return;
|
|
}
|
|
|
|
// LYC
|
|
case 0x45: {
|
|
if (register_LYC != value)
|
|
{
|
|
gbMemory[0xff45] = register_LYC = value;
|
|
if(register_LCDC & 0x80) {
|
|
gbCompareLYToLYC();
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
// DMA!
|
|
case 0x46: {
|
|
int source = value * 0x0100;
|
|
|
|
gbCopyMemory(0xfe00,
|
|
source,
|
|
0xa0);
|
|
gbMemory[0xff46] = register_DMA = value;
|
|
return;
|
|
}
|
|
|
|
// BGP
|
|
case 0x47: {
|
|
|
|
int temp = -1;
|
|
|
|
gbMemory[0xff47] = value;
|
|
|
|
if (gbLcdModeDelayed == 3)
|
|
temp = ((GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS) -
|
|
gbLcdLYIncrementTicksDelayed);
|
|
|
|
if (temp >=0)
|
|
{
|
|
for (int i=temp<<(gbSpeed ? 1 : 2);i<300;i++)
|
|
if (temp < 300)
|
|
gbBgpLine[i] = value;
|
|
}
|
|
else
|
|
memset(gbBgpLine,value,sizeof(gbBgpLine));
|
|
|
|
gbSetBGPalette(value);
|
|
break;
|
|
}
|
|
|
|
// OBP0
|
|
case 0x48: {
|
|
int temp = -1;
|
|
|
|
gbMemory[0xff48] = value;
|
|
|
|
if (gbLcdModeDelayed == 3)
|
|
temp = ((GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS) -
|
|
gbLcdLYIncrementTicksDelayed);
|
|
|
|
if (temp >=0)
|
|
{
|
|
for (int i=temp<<(gbSpeed ? 1 : 2);i<300;i++)
|
|
if (temp < 300)
|
|
gbObp0Line[i] = value;
|
|
}
|
|
else
|
|
memset(gbObp0Line,value,sizeof(gbObp0Line));
|
|
|
|
gbSetObj0Palette(value);
|
|
break;
|
|
}
|
|
|
|
// OBP1
|
|
case 0x49: {
|
|
int temp = -1;
|
|
|
|
gbMemory[0xff49] = value;
|
|
|
|
if (gbLcdModeDelayed == 3)
|
|
temp = ((GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS) -
|
|
gbLcdLYIncrementTicksDelayed);
|
|
|
|
if (temp >=0)
|
|
{
|
|
for (int i=temp<<(gbSpeed ? 1 : 2);i<300;i++)
|
|
if (temp < 300)
|
|
gbObp1Line[i] = value;
|
|
}
|
|
else
|
|
memset(gbObp1Line,value,sizeof(gbObp1Line));
|
|
|
|
gbSetObj1Palette(value);
|
|
break;
|
|
}
|
|
|
|
// WY
|
|
case 0x4a:
|
|
gbMemory[0xff4a] = register_WY = value;
|
|
if ((register_LY <= register_WY) && ((gbWindowLine < 0) || (gbWindowLine>=144)))
|
|
{
|
|
gbWindowLine = -1;
|
|
oldRegister_WY = register_WY;
|
|
}
|
|
return;
|
|
|
|
// WX
|
|
case 0x4b:
|
|
gbMemory[0xff4b] = register_WX = value;
|
|
return;
|
|
|
|
// KEY1
|
|
case 0x4d: {
|
|
if(gbCgbMode) {
|
|
gbMemory[0xff4d] = (gbMemory[0xff4d] & 0x80) | (value & 1) | 0x7e;
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
|
|
// VBK
|
|
case 0x4f: {
|
|
if(gbCgbMode) {
|
|
value = value & 1;
|
|
if(value == gbVramBank)
|
|
return;
|
|
|
|
int vramAddress = value * 0x2000;
|
|
gbMemoryMap[0x08] = &gbVram[vramAddress];
|
|
gbMemoryMap[0x09] = &gbVram[vramAddress + 0x1000];
|
|
|
|
gbVramBank = value;
|
|
register_VBK = value;
|
|
}
|
|
return;
|
|
}
|
|
break;
|
|
|
|
// BOOTROM disable register (also gbCgbMode enabler if value & 0x10 ?)
|
|
case 0x50 :
|
|
{
|
|
if (useBios && inBios && !skipBios && (value & 1))
|
|
{
|
|
gbMemoryMap[0x00] = &gbRom[0x0000];
|
|
memcpy ((u8 *)(gbRom+0x100), (u8 *)(gbMemory + 0x100), 0xF00);
|
|
inBios = false;
|
|
}
|
|
}
|
|
|
|
// HDMA1
|
|
case 0x51: {
|
|
if(gbCgbMode) {
|
|
if(value > 0x7f && value < 0xa0)
|
|
value = 0;
|
|
|
|
gbHdmaSource = (value << 8) | (gbHdmaSource & 0xf0);
|
|
|
|
register_HDMA1 = value;
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
|
|
// HDMA2
|
|
case 0x52: {
|
|
if(gbCgbMode) {
|
|
value = value & 0xf0;
|
|
|
|
gbHdmaSource = (gbHdmaSource & 0xff00) | (value);
|
|
|
|
register_HDMA2 = value;
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
|
|
// HDMA3
|
|
case 0x53: {
|
|
if(gbCgbMode) {
|
|
value = value & 0x1f;
|
|
gbHdmaDestination = (value << 8) | (gbHdmaDestination & 0xf0);
|
|
gbHdmaDestination |= 0x8000;
|
|
register_HDMA3 = value;
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
|
|
// HDMA4
|
|
case 0x54: {
|
|
if(gbCgbMode) {
|
|
value = value & 0xf0;
|
|
gbHdmaDestination = (gbHdmaDestination & 0x1f00) | value;
|
|
gbHdmaDestination |= 0x8000;
|
|
register_HDMA4 = value;
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
|
|
// HDMA5
|
|
case 0x55: {
|
|
|
|
if(gbCgbMode) {
|
|
gbHdmaBytes = 16 + (value & 0x7f) * 16;
|
|
if(gbHdmaOn) {
|
|
if(value & 0x80) {
|
|
gbMemory[0xff55] = register_HDMA5 = (value & 0x7f);
|
|
} else {
|
|
register_HDMA5 = 0xff;
|
|
gbHdmaOn = 0;
|
|
}
|
|
} else {
|
|
if(value & 0x80) {
|
|
gbHdmaOn = 1;
|
|
gbMemory[0xff55] = register_HDMA5 = value & 0x7f;
|
|
if(gbLcdModeDelayed == 0)
|
|
gbDoHdma();
|
|
} else {
|
|
// we need to take the time it takes to complete the transfer into
|
|
// account... according to GB DEV FAQs, the setup time is the same
|
|
// for single and double speed, but the actual transfer takes the
|
|
// same time
|
|
if(gbSpeed)
|
|
gbDmaTicks = 2+16 * ((value & 0x7f) +1);
|
|
else
|
|
gbDmaTicks = 1+8 * ((value & 0x7f)+1);
|
|
|
|
gbCopyMemory((gbHdmaDestination & 0x1ff0) | 0x8000,
|
|
gbHdmaSource & 0xfff0,
|
|
gbHdmaBytes);
|
|
gbHdmaDestination += gbHdmaBytes;
|
|
gbHdmaSource += gbHdmaBytes;
|
|
|
|
gbMemory[0xff51] = register_HDMA1 = 0xff;// = (gbHdmaSource >> 8) & 0xff;
|
|
gbMemory[0xff52] = register_HDMA2 = 0xff;// = gbHdmaSource & 0xf0;
|
|
gbMemory[0xff53] = register_HDMA3 = 0xff;// = ((gbHdmaDestination - 0x8000) >> 8) & 0x1f;
|
|
gbMemory[0xff54] = register_HDMA4 = 0xff;// = gbHdmaDestination & 0xf0;
|
|
gbMemory[0xff55] = register_HDMA5 = 0xff;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
|
|
// BCPS
|
|
case 0x68: {
|
|
if(gbCgbMode) {
|
|
int paletteIndex = (value & 0x3f) >> 1;
|
|
int paletteHiLo = (value & 0x01);
|
|
|
|
gbMemory[0xff68] = value;
|
|
|
|
gbMemory[0xff69] = (paletteHiLo ?
|
|
(gbPalette[paletteIndex] >> 8) :
|
|
(gbPalette[paletteIndex] & 0x00ff));
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
|
|
// BCPD
|
|
case 0x69: {
|
|
if(gbCgbMode) {
|
|
int v = gbMemory[0xff68];
|
|
int paletteIndex = (v & 0x3f) >> 1;
|
|
int paletteHiLo = (v & 0x01);
|
|
|
|
// No access to gbPalette during mode 3 (Color Panel Demo)
|
|
// CAK - The following check has to be commented out for
|
|
// colourised roms like Metroid 2 DX
|
|
//if (((gbLcdModeDelayed != 3) && (!((gbLcdMode == 0) && (gbLcdTicks>=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-1)))) && (!gbSpeed)) ||
|
|
// (gbSpeed && ((gbLcdMode == 1) || (gbLcdMode == 2) ||
|
|
// ((gbLcdMode == 3) && (gbLcdTicks>(GBLCD_MODE_3_CLOCK_TICKS-2))) ||
|
|
// ((gbLcdMode == 0) && (gbLcdTicks<=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-2))))))
|
|
{
|
|
gbMemory[0xff69] = value;
|
|
gbPalette[paletteIndex] = (paletteHiLo ?
|
|
((value << 8) | (gbPalette[paletteIndex] & 0xff)) :
|
|
((gbPalette[paletteIndex] & 0xff00) | (value))) & 0x7fff;
|
|
}
|
|
|
|
|
|
if(gbMemory[0xff68] & 0x80) {
|
|
int index = ((gbMemory[0xff68] & 0x3f) + 1) & 0x3f;
|
|
|
|
gbMemory[0xff68] = (gbMemory[0xff68] & 0x80) | index;
|
|
gbMemory[0xff69] = (index & 1 ?
|
|
(gbPalette[index>>1] >> 8) :
|
|
(gbPalette[index>>1] & 0x00ff));
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
|
|
// OCPS
|
|
case 0x6a: {
|
|
if(gbCgbMode) {
|
|
int paletteIndex = (value & 0x3f) >> 1;
|
|
int paletteHiLo = (value & 0x01);
|
|
|
|
paletteIndex += 32;
|
|
|
|
gbMemory[0xff6a] = value;
|
|
|
|
gbMemory[0xff6b] = (paletteHiLo ?
|
|
(gbPalette[paletteIndex] >> 8) :
|
|
(gbPalette[paletteIndex] & 0x00ff));
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
|
|
// OCPD
|
|
case 0x6b: {
|
|
|
|
if(gbCgbMode) {
|
|
int v = gbMemory[0xff6a];
|
|
int paletteIndex = (v & 0x3f) >> 1;
|
|
int paletteHiLo = (v & 0x01);
|
|
|
|
paletteIndex += 32;
|
|
|
|
// No access to gbPalette during mode 3 (Color Panel Demo)
|
|
// CAK - The following check has to be commented out for
|
|
// colourised roms like Metroid 2 DX
|
|
//if (((gbLcdModeDelayed != 3) && (!((gbLcdMode == 0) && (gbLcdTicks>=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-1)))) && (!gbSpeed)) ||
|
|
// (gbSpeed && ((gbLcdMode == 1) || (gbLcdMode == 2) ||
|
|
// ((gbLcdMode == 3) && (gbLcdTicks>(GBLCD_MODE_3_CLOCK_TICKS-2))) ||
|
|
// ((gbLcdMode == 0) && (gbLcdTicks<=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-2))))))
|
|
{
|
|
gbMemory[0xff6b] = value;
|
|
gbPalette[paletteIndex] = (paletteHiLo ?
|
|
((value << 8) | (gbPalette[paletteIndex] & 0xff)) :
|
|
((gbPalette[paletteIndex] & 0xff00) | (value))) & 0x7fff;
|
|
}
|
|
|
|
if(gbMemory[0xff6a] & 0x80) {
|
|
int index = ((gbMemory[0xff6a] & 0x3f) + 1) & 0x3f;
|
|
|
|
gbMemory[0xff6a] = (gbMemory[0xff6a] & 0x80) | index;
|
|
|
|
gbMemory[0xff6b] = (index & 1 ?
|
|
(gbPalette[(index>>1) + 32] >> 8) :
|
|
(gbPalette[(index>>1) + 32] & 0x00ff));
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 0x6c: {
|
|
gbMemory[0xff6c] = 0xfe | value;
|
|
return;
|
|
}
|
|
|
|
|
|
// SVBK
|
|
case 0x70: {
|
|
if(gbCgbMode) {
|
|
value = value & 7;
|
|
|
|
int bank = value;
|
|
if(value == 0)
|
|
bank = 1;
|
|
|
|
if(bank == gbWramBank)
|
|
return;
|
|
|
|
int wramAddress = bank * 0x1000;
|
|
gbMemoryMap[0x0d] = &gbWram[wramAddress];
|
|
|
|
gbWramBank = bank;
|
|
gbMemory[0xff70] = register_SVBK = value;
|
|
return;
|
|
}
|
|
}
|
|
|
|
case 0x75:{
|
|
gbMemory[0xff75] = 0x8f | value;
|
|
return;
|
|
}
|
|
|
|
case 0xff: {
|
|
gbMemory[0xffff] = register_IE = value;
|
|
return;
|
|
}
|
|
}
|
|
|
|
if(address < 0xff80)
|
|
{
|
|
gbMemory[address] = value;
|
|
return;
|
|
}
|
|
|
|
gbMemory[address] = value;
|
|
}
|
|
|
|
u8 gbReadOpcode(u16 address)
|
|
{
|
|
if(gbCheatMap[address])
|
|
return gbCheatRead(address);
|
|
|
|
if(address < 0x8000)
|
|
return gbMemoryMap[address>>12][address&0x0fff];
|
|
|
|
if(address < 0xa000)
|
|
{
|
|
// A lot of 'ugly' checks... But only way to emulate this particular behaviour...
|
|
if (
|
|
(
|
|
(gbHardware & 0xa) &&
|
|
(
|
|
(gbLcdModeDelayed != 3) ||
|
|
(
|
|
((register_LY == 0) && (gbScreenOn == false) && (register_LCDC & 0x80)) &&
|
|
(gbLcdLYIncrementTicksDelayed == (GBLY_INCREMENT_CLOCK_TICKS - GBLCD_MODE_2_CLOCK_TICKS))
|
|
)
|
|
)
|
|
)
|
|
||
|
|
(
|
|
(gbHardware & 0x5) &&
|
|
(gbLcdModeDelayed != 3) &&
|
|
(
|
|
(gbLcdMode != 3) ||
|
|
((register_LY == 0) && ((gbScreenOn == false) && (register_LCDC & 0x80)) &&
|
|
(gbLcdLYIncrementTicks == (GBLY_INCREMENT_CLOCK_TICKS - GBLCD_MODE_2_CLOCK_TICKS)))
|
|
)
|
|
)
|
|
)
|
|
return gbMemoryMap[address >> 12][address & 0x0fff];
|
|
|
|
return 0xff;
|
|
}
|
|
|
|
// Used for the mirroring of 0xC000 in 0xE000
|
|
if ((address >= 0xe000) && (address < 0xfe00))
|
|
address &= ~0x2000;
|
|
|
|
switch(address & 0xf000) {
|
|
case 0x0a:
|
|
case 0x0b:
|
|
if(mapperReadRAM)
|
|
return mapperReadRAM(address);
|
|
break;
|
|
case 0x0f:
|
|
if(address > 0xff00) {
|
|
switch(address & 0x00ff) {
|
|
case 0x02:
|
|
return (gbMemory[0xff02]);
|
|
case 0x03:
|
|
return (0xff);
|
|
case 0x04:
|
|
return register_DIV;
|
|
case 0x05:
|
|
return register_TIMA;
|
|
case 0x06:
|
|
return register_TMA;
|
|
case 0x07:
|
|
return (0xf8 | register_TAC);
|
|
case 0x08:
|
|
case 0x09:
|
|
case 0x0a:
|
|
case 0x0b:
|
|
case 0x0c:
|
|
case 0x0d:
|
|
case 0x0e:
|
|
return (0xff);
|
|
case 0x0f:
|
|
return (0xe0 | gbMemory[0xff0f]);
|
|
case 0x40:
|
|
return register_LCDC;
|
|
case 0x41:
|
|
// This is a GB/C only bug (ie. not GBA/SP).
|
|
if ((gbHardware & 7) && (gbLcdMode == 2) && (gbLcdModeDelayed == 1) && (!gbSpeed))
|
|
return (0x80 | (gbMemory[0xff41] & 0xFC));
|
|
else
|
|
return (0x80 | gbMemory[0xff41]);
|
|
case 0x42:
|
|
return register_SCY;
|
|
case 0x43:
|
|
return register_SCX;
|
|
case 0x44:
|
|
if (((gbHardware & 7) && ((gbLcdMode == 1) && (gbLcdTicks == 0x71))) ||
|
|
(!(register_LCDC & 0x80)))
|
|
return 0;
|
|
else
|
|
return register_LY;
|
|
case 0x45:
|
|
return register_LYC;
|
|
case 0x46:
|
|
return register_DMA;
|
|
case 0x4a:
|
|
return register_WY;
|
|
case 0x4b:
|
|
return register_WX;
|
|
case 0x4c:
|
|
return 0xff;
|
|
case 0x4f:
|
|
return (0xfe | register_VBK);
|
|
case 0x51:
|
|
return register_HDMA1;
|
|
case 0x52:
|
|
return register_HDMA2;
|
|
case 0x53:
|
|
return register_HDMA3;
|
|
case 0x54:
|
|
return register_HDMA4;
|
|
case 0x55:
|
|
return register_HDMA5;
|
|
case 0x68:
|
|
case 0x6a:
|
|
if (gbCgbMode)
|
|
return (0x40 | gbMemory[address]);
|
|
else
|
|
return 0xc0;
|
|
case 0x69:
|
|
case 0x6b:
|
|
if (gbCgbMode)
|
|
{
|
|
// No access to gbPalette during mode 3 (Color Panel Demo)
|
|
//if (((gbLcdModeDelayed != 3) && (!((gbLcdMode == 0) && (gbLcdTicks>=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-1)))) && (!gbSpeed)) ||
|
|
// (gbSpeed && ((gbLcdMode == 1) || (gbLcdMode == 2) ||
|
|
// ((gbLcdMode == 3) && (gbLcdTicks>(GBLCD_MODE_3_CLOCK_TICKS-2))) ||
|
|
// ((gbLcdMode == 0) && (gbLcdTicks<=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-2))))))
|
|
return (gbMemory[address]);
|
|
//else
|
|
// return 0xff;
|
|
}
|
|
else
|
|
return 0xff;
|
|
case 0x70:
|
|
if (gbCgbMode)
|
|
return (0xf8 | register_SVBK);
|
|
else
|
|
return 0xff;
|
|
case 0xff:
|
|
return register_IE;
|
|
}
|
|
}
|
|
// OAM not accessible during mode 2 & 3.
|
|
if(((address >= 0xfe00) && (address<0xfea0)) &&
|
|
((gbLcdMode | gbLcdModeDelayed) &2))
|
|
return 0xff;
|
|
break;
|
|
}
|
|
|
|
if ((address >= 0xfea0) && (address < 0xff00))
|
|
{
|
|
if (gbHardware & 1)
|
|
return ((((address + ((address >> 4) - 0xfea)) >> 2) & 1) ? 0x00 : 0xff );
|
|
else if (gbHardware & 2)
|
|
return gbMemoryMap[address>>12][address & 0x0fff];
|
|
else if (gbHardware & 4)
|
|
return ((((address + ((address >> 4) - 0xfea)) >> 2) & 1) ? 0xff : 0x00 );
|
|
else if (gbHardware & 8)
|
|
return ((address & 0xf0) |((address & 0xf0)>>4));
|
|
}
|
|
|
|
return gbMemoryMap[address>>12][address & 0x0fff];
|
|
}
|
|
|
|
u8 gbReadMemory(u16 address)
|
|
{
|
|
if(gbCheatMap[address])
|
|
return gbCheatRead(address);
|
|
|
|
|
|
if(address < 0x8000)
|
|
return gbMemoryMap[address>>12][address&0x0fff];
|
|
|
|
if(address < 0xa000)
|
|
{
|
|
// A lot of 'ugly' checks... But only way to emulate this particular behaviour...
|
|
if (
|
|
(
|
|
(gbHardware & 0xa) &&
|
|
(
|
|
(gbLcdModeDelayed != 3) ||
|
|
(
|
|
((register_LY == 0) && (gbScreenOn == false) && (register_LCDC & 0x80)) &&
|
|
(gbLcdLYIncrementTicksDelayed == (GBLY_INCREMENT_CLOCK_TICKS - GBLCD_MODE_2_CLOCK_TICKS))
|
|
)
|
|
)
|
|
)
|
|
||
|
|
(
|
|
(gbHardware & 0x5) &&
|
|
(gbLcdModeDelayed != 3) &&
|
|
(
|
|
(gbLcdMode != 3) ||
|
|
((register_LY == 0) && ((gbScreenOn == false) && (register_LCDC & 0x80)) &&
|
|
(gbLcdLYIncrementTicks == (GBLY_INCREMENT_CLOCK_TICKS - GBLCD_MODE_2_CLOCK_TICKS)))
|
|
)
|
|
)
|
|
)
|
|
return gbMemoryMap[address>>12][address&0x0fff];
|
|
return 0xff;
|
|
}
|
|
|
|
if ((address >= 0xe000) && (address < 0xfe00))
|
|
address &= ~0x2000;
|
|
|
|
if(address < 0xc000) {
|
|
#ifndef FINAL_VERSION
|
|
if(memorydebug) {
|
|
log("Memory register read %04x PC=%04x\n",
|
|
address,
|
|
PC.W);
|
|
}
|
|
#endif
|
|
|
|
// for the 2kb ram limit (fixes crash in shawu's story
|
|
// but now its sram test fails, as the it expects 8kb and not 2kb...
|
|
// So use the 'genericflashcard' option to fix it).
|
|
if (address<=(0xa000+gbRamSizeMask))
|
|
{
|
|
if(mapperReadRAM)
|
|
return mapperReadRAM(address);
|
|
return gbMemoryMap[address>>12][address & 0x0fff];
|
|
}
|
|
return 0xff;
|
|
}
|
|
|
|
if(address >= 0xff00) {
|
|
if ( address >= 0xFF10 && address <= 0xFF3F )
|
|
return gbSoundRead( address );
|
|
|
|
switch(address & 0x00ff) {
|
|
case 0x00:
|
|
{
|
|
if(gbSgbMode) {
|
|
gbSgbReadingController |= 4;
|
|
gbSgbResetPacketState();
|
|
}
|
|
|
|
int b = gbMemory[0xff00];
|
|
|
|
if((b & 0x30) == 0x20) {
|
|
b &= 0xf0;
|
|
|
|
int joy = 0;
|
|
if(gbSgbMode && gbSgbMultiplayer) {
|
|
switch(gbSgbNextController) {
|
|
case 0x0f:
|
|
joy = 0;
|
|
break;
|
|
case 0x0e:
|
|
joy = 1;
|
|
break;
|
|
case 0x0d:
|
|
joy = 2;
|
|
break;
|
|
case 0x0c:
|
|
joy = 3;
|
|
break;
|
|
default:
|
|
joy = 0;
|
|
}
|
|
}
|
|
int joystate = gbJoymask[joy];
|
|
if(!(joystate & 128))
|
|
b |= 0x08;
|
|
if(!(joystate & 64))
|
|
b |= 0x04;
|
|
if(!(joystate & 32))
|
|
b |= 0x02;
|
|
if(!(joystate & 16))
|
|
b |= 0x01;
|
|
|
|
gbMemory[0xff00] = b;
|
|
} else if((b & 0x30) == 0x10) {
|
|
b &= 0xf0;
|
|
|
|
int joy = 0;
|
|
if(gbSgbMode && gbSgbMultiplayer) {
|
|
switch(gbSgbNextController) {
|
|
case 0x0f:
|
|
joy = 0;
|
|
break;
|
|
case 0x0e:
|
|
joy = 1;
|
|
break;
|
|
case 0x0d:
|
|
joy = 2;
|
|
break;
|
|
case 0x0c:
|
|
joy = 3;
|
|
break;
|
|
default:
|
|
joy = 0;
|
|
}
|
|
}
|
|
int joystate = gbJoymask[joy];
|
|
if(!(joystate & 8))
|
|
b |= 0x08;
|
|
if(!(joystate & 4))
|
|
b |= 0x04;
|
|
if(!(joystate & 2))
|
|
b |= 0x02;
|
|
if(!(joystate & 1))
|
|
b |= 0x01;
|
|
|
|
gbMemory[0xff00] = b;
|
|
} else {
|
|
if(gbSgbMode && gbSgbMultiplayer) {
|
|
gbMemory[0xff00] = 0xf0 | gbSgbNextController;
|
|
} else {
|
|
gbMemory[0xff00] = 0xff;
|
|
}
|
|
}
|
|
}
|
|
return gbMemory[0xff00];
|
|
break;
|
|
case 0x01:
|
|
return gbMemory[0xff01];
|
|
case 0x02:
|
|
return (gbMemory[0xff02]);
|
|
case 0x04:
|
|
return register_DIV;
|
|
case 0x05:
|
|
return register_TIMA;
|
|
case 0x06:
|
|
return register_TMA;
|
|
case 0x07:
|
|
return (0xf8 | register_TAC);
|
|
case 0x0f:
|
|
return (0xe0 | gbMemory[0xff0f]);
|
|
case 0x30:
|
|
case 0x31:
|
|
case 0x32:
|
|
case 0x33:
|
|
case 0x34:
|
|
case 0x35:
|
|
case 0x36:
|
|
case 0x37:
|
|
case 0x38:
|
|
case 0x39:
|
|
case 0x3A:
|
|
case 0x3B:
|
|
case 0x3C:
|
|
case 0x3D:
|
|
case 0x3E:
|
|
case 0x3F:
|
|
if ((gbMemory[NR30] & 0x80) && (gbMemory[NR34] & 0x80))
|
|
return 0xFF;
|
|
else
|
|
return gbMemoryMap[address>>12][address & 0x0fff];
|
|
case 0x40:
|
|
return register_LCDC;
|
|
case 0x41:
|
|
// This is a GB/C only bug (ie. not GBA/SP).
|
|
if ((gbHardware & 7) && (gbLcdMode == 2) && (gbLcdModeDelayed == 1) && (!gbSpeed))
|
|
return (0x80 | (gbMemory[0xff41] & 0xFC));
|
|
else
|
|
return (0x80 | gbMemory[0xff41]);
|
|
case 0x42:
|
|
return register_SCY;
|
|
case 0x43:
|
|
return register_SCX;
|
|
case 0x44:
|
|
if (((gbHardware & 7) && ((gbLcdMode == 1) && (gbLcdTicks == 0x71))) ||
|
|
(!(register_LCDC & 0x80)))
|
|
return (0);
|
|
else
|
|
return register_LY;
|
|
case 0x45:
|
|
return register_LYC;
|
|
case 0x46:
|
|
return register_DMA;
|
|
case 0x4a:
|
|
return register_WY;
|
|
case 0x4b:
|
|
return register_WX;
|
|
case 0x4f:
|
|
return (0xfe | register_VBK);
|
|
case 0x51:
|
|
return register_HDMA1;
|
|
case 0x52:
|
|
return register_HDMA2;
|
|
case 0x53:
|
|
return register_HDMA3;
|
|
case 0x54:
|
|
return register_HDMA4;
|
|
case 0x55:
|
|
return register_HDMA5;
|
|
case 0x68:
|
|
case 0x6a:
|
|
if (gbCgbMode)
|
|
return (0x40 | gbMemory[address]);
|
|
else
|
|
return 0xc0;
|
|
case 0x69:
|
|
case 0x6b:
|
|
if (gbCgbMode)
|
|
{
|
|
// No access to gbPalette during mode 3 (Color Panel Demo)
|
|
//if (((gbLcdModeDelayed != 3) && (!((gbLcdMode == 0) && (gbLcdTicks>=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-1)))) && (!gbSpeed)) ||
|
|
// (gbSpeed && ((gbLcdMode == 1) || (gbLcdMode == 2) ||
|
|
// ((gbLcdMode == 3) && (gbLcdTicks>(GBLCD_MODE_3_CLOCK_TICKS-2))) ||
|
|
// ((gbLcdMode == 0) && (gbLcdTicks<=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-2))))))
|
|
return (gbMemory[address]);
|
|
//else
|
|
// return 0xff;
|
|
}
|
|
else
|
|
return 0xff;
|
|
case 0x70:
|
|
if (gbCgbMode)
|
|
return (0xf8 | register_SVBK);
|
|
else
|
|
return 0xff;
|
|
case 0xff:
|
|
return register_IE;
|
|
}
|
|
}
|
|
// OAM not accessible during mode 2 & 3.
|
|
if(((address >= 0xfe00) && (address<0xfea0)) &&
|
|
((((gbLcdMode | gbLcdModeDelayed) & 2) &&
|
|
(!(gbSpeed && (gbHardware & 0x2) && !(gbLcdModeDelayed & 2) && (gbLcdMode == 2)))) ||
|
|
(gbSpeed && (gbHardware & 0x2) && (gbLcdModeDelayed == 0) && (gbLcdTicksDelayed == (GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299])))))
|
|
return 0xff;
|
|
|
|
if ((address >= 0xfea0) && (address < 0xff00))
|
|
{
|
|
if (gbHardware & 1)
|
|
return ((((address + ((address >> 4) - 0xfea)) >> 2) & 1) ? 0x00 : 0xff );
|
|
else if (gbHardware & 2)
|
|
return gbMemoryMap[address>>12][address & 0x0fff];
|
|
else if (gbHardware & 4)
|
|
return ((((address + ((address >> 4) - 0xfea)) >> 2) & 1) ? 0xff : 0x00 );
|
|
else if (gbHardware & 8)
|
|
return ((address & 0xf0) |((address & 0xf0)>>4));
|
|
}
|
|
|
|
return gbMemoryMap[address>>12][address & 0x0fff];
|
|
}
|
|
|
|
void gbVblank_interrupt()
|
|
{
|
|
gbCheatWrite(false); // Emulates GS codes.
|
|
gbMemory[0xff0f] = register_IF &= 0xfe;
|
|
gbWriteMemory(--SP.W, PC.B.B1);
|
|
gbWriteMemory(--SP.W, PC.B.B0);
|
|
PC.W = 0x40;
|
|
}
|
|
|
|
void gbLcd_interrupt()
|
|
{
|
|
gbCheatWrite(false); // Emulates GS codes.
|
|
gbMemory[0xff0f] = register_IF &= 0xfd;
|
|
gbWriteMemory(--SP.W, PC.B.B1);
|
|
gbWriteMemory(--SP.W, PC.B.B0);
|
|
PC.W = 0x48;
|
|
}
|
|
|
|
void gbTimer_interrupt()
|
|
{
|
|
gbMemory[0xff0f] = register_IF &= 0xfb;
|
|
gbWriteMemory(--SP.W, PC.B.B1);
|
|
gbWriteMemory(--SP.W, PC.B.B0);
|
|
PC.W = 0x50;
|
|
}
|
|
|
|
void gbSerial_interrupt()
|
|
{
|
|
gbMemory[0xff0f] = register_IF &= 0xf7;
|
|
gbWriteMemory(--SP.W, PC.B.B1);
|
|
gbWriteMemory(--SP.W, PC.B.B0);
|
|
PC.W = 0x58;
|
|
}
|
|
|
|
void gbJoypad_interrupt()
|
|
{
|
|
gbMemory[0xff0f] = register_IF &= 0xef;
|
|
gbWriteMemory(--SP.W, PC.B.B1);
|
|
gbWriteMemory(--SP.W, PC.B.B0);
|
|
PC.W = 0x60;
|
|
}
|
|
|
|
void gbSpeedSwitch()
|
|
{
|
|
gbBlackScreen = true;
|
|
if(gbSpeed == 0) {
|
|
gbSpeed = 1;
|
|
GBLCD_MODE_0_CLOCK_TICKS = 51 * 2;
|
|
GBLCD_MODE_1_CLOCK_TICKS = 1140 * 2;
|
|
GBLCD_MODE_2_CLOCK_TICKS = 20 * 2;
|
|
GBLCD_MODE_3_CLOCK_TICKS = 43 * 2;
|
|
GBLY_INCREMENT_CLOCK_TICKS = 114 * 2;
|
|
GBDIV_CLOCK_TICKS = 64;
|
|
GBTIMER_MODE_0_CLOCK_TICKS = 256;
|
|
GBTIMER_MODE_1_CLOCK_TICKS = 4;
|
|
GBTIMER_MODE_2_CLOCK_TICKS = 16;
|
|
GBTIMER_MODE_3_CLOCK_TICKS = 64;
|
|
GBSERIAL_CLOCK_TICKS = 128 * 2;
|
|
gbLcdTicks *= 2;
|
|
gbLcdTicksDelayed *=2;
|
|
gbLcdTicksDelayed--;
|
|
gbLcdLYIncrementTicks *= 2;
|
|
gbLcdLYIncrementTicksDelayed *= 2;
|
|
gbLcdLYIncrementTicksDelayed--;
|
|
gbSerialTicks *= 2;
|
|
//SOUND_CLOCK_TICKS = soundQuality * 24 * 2;
|
|
//soundTicks *= 2;
|
|
gbLine99Ticks = 3;
|
|
} else {
|
|
gbSpeed = 0;
|
|
GBLCD_MODE_0_CLOCK_TICKS = 51;
|
|
GBLCD_MODE_1_CLOCK_TICKS = 1140;
|
|
GBLCD_MODE_2_CLOCK_TICKS = 20;
|
|
GBLCD_MODE_3_CLOCK_TICKS = 43;
|
|
GBLY_INCREMENT_CLOCK_TICKS = 114;
|
|
GBDIV_CLOCK_TICKS = 64;
|
|
GBTIMER_MODE_0_CLOCK_TICKS = 256;
|
|
GBTIMER_MODE_1_CLOCK_TICKS = 4;
|
|
GBTIMER_MODE_2_CLOCK_TICKS = 16;
|
|
GBTIMER_MODE_3_CLOCK_TICKS = 64;
|
|
GBSERIAL_CLOCK_TICKS = 128;
|
|
gbLcdTicks >>= 1;
|
|
gbLcdTicksDelayed++;
|
|
gbLcdTicksDelayed >>=1;
|
|
gbLcdLYIncrementTicks >>= 1;
|
|
gbLcdLYIncrementTicksDelayed++;
|
|
gbLcdLYIncrementTicksDelayed >>= 1;
|
|
gbSerialTicks /= 2;
|
|
//SOUND_CLOCK_TICKS = soundQuality * 24;
|
|
//soundTicks /= 2;
|
|
gbLine99Ticks = 1;
|
|
if (gbHardware & 8)
|
|
gbLine99Ticks++;
|
|
}
|
|
gbDmaTicks += (134)*GBLY_INCREMENT_CLOCK_TICKS + (37<<(gbSpeed ? 1 : 0));
|
|
}
|
|
|
|
bool CPUIsGBBios(const char * file)
|
|
{
|
|
if(strlen(file) > 4) {
|
|
const char * p = strrchr(file,'.');
|
|
|
|
if(p != NULL) {
|
|
if(_stricmp(p, ".gb") == 0)
|
|
return true;
|
|
if(_stricmp(p, ".bin") == 0)
|
|
return true;
|
|
if(_stricmp(p, ".bios") == 0)
|
|
return true;
|
|
if(_stricmp(p, ".rom") == 0)
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void gbCPUInit(const char *biosFileName, bool useBiosFile)
|
|
{
|
|
useBios = false;
|
|
if (useBiosFile)
|
|
{
|
|
/* int size = 0x100;
|
|
if(utilLoad(biosFileName,
|
|
CPUIsGBBios,
|
|
bios,
|
|
size)) {
|
|
if(size == 0x100)
|
|
useBios = true;
|
|
else
|
|
systemMessage(MSG_INVALID_BIOS_FILE_SIZE, N_("Invalid BOOTROM file size"));
|
|
}*/
|
|
}
|
|
}
|
|
|
|
void gbGetHardwareType()
|
|
{
|
|
gbCgbMode = 0;
|
|
gbSgbMode = 0;
|
|
if(gbRom[0x143] & 0x80) {
|
|
if((gbEmulatorType == 0) ||
|
|
gbEmulatorType == 1 ||
|
|
gbEmulatorType == 4) {
|
|
gbCgbMode = 1;
|
|
}
|
|
}
|
|
|
|
if((gbCgbMode == 0 ) && (gbRom[0x146] == 0x03)) {
|
|
if(gbEmulatorType == 0 ||
|
|
gbEmulatorType == 2 ||
|
|
gbEmulatorType == 5)
|
|
gbSgbMode = 1;
|
|
gbCgbMode = 0;
|
|
}
|
|
|
|
gbHardware = 1; // GB
|
|
if (((gbCgbMode == 1) && (gbEmulatorType == 0)) || (gbEmulatorType == 1))
|
|
gbHardware = 2; // GBC
|
|
else if (((gbSgbMode == 1) && (gbEmulatorType == 0)) || (gbEmulatorType == 2) || (gbEmulatorType == 5))
|
|
gbHardware = 4; // SGB(2)
|
|
else if (gbEmulatorType == 4)
|
|
gbHardware = 8; // GBA
|
|
|
|
gbGBCColorType = 0;
|
|
if (gbHardware & 8) // If GBA is selected, choose the GBA default settings.
|
|
gbGBCColorType = 2; // (0 = GBC, 1 = GBA, 2 = GBASP)
|
|
}
|
|
|
|
void gbReset()
|
|
{
|
|
systemCartridgeRumble(false);
|
|
gbGetHardwareType();
|
|
gbPaletteReset();
|
|
|
|
oldRegister_WY = 146;
|
|
gbInterruptLaunched = 0;
|
|
|
|
if(gbCgbMode == 1) {
|
|
if (gbVram == NULL)
|
|
gbVram = (u8 *)malloc(0x4000);
|
|
if (gbWram == NULL)
|
|
gbWram = (u8 *)malloc(0x8000);
|
|
memset(gbVram,0,0x4000);
|
|
memset(gbPalette,0, 2*128);
|
|
}
|
|
else
|
|
{
|
|
if(gbVram != NULL) {
|
|
free(gbVram);
|
|
gbVram = NULL;
|
|
}
|
|
if(gbWram != NULL) {
|
|
free(gbWram);
|
|
gbWram = NULL;
|
|
}
|
|
}
|
|
|
|
gbLYChangeHappened = false;
|
|
gbLCDChangeHappened = false;
|
|
gbBlackScreen = false;
|
|
gbInterruptWait = 0;
|
|
gbDmaTicks = 0;
|
|
clockTicks = 0;
|
|
|
|
// clean Wram
|
|
// This kinda emulates the startup state of Wram on GB/C (not very accurate,
|
|
// but way closer to the reality than filling it with 00es or FFes).
|
|
// On GBA/GBASP, it's kinda filled with random data.
|
|
// In all cases, most of the 2nd bank is filled with 00s.
|
|
// The starting data are important for some 'buggy' games, like Buster Brothers or
|
|
// Karamuchou ha Oosawagi!.
|
|
if (gbMemory != NULL)
|
|
{
|
|
memset(gbMemory,0xff, 65536);
|
|
for (int temp = 0xC000; temp < 0xE000; temp++)
|
|
if ((temp & 0x8) ^((temp & 0x800)>>8))
|
|
{
|
|
if ((gbHardware & 0x02) && (gbGBCColorType == 0))
|
|
gbMemory[temp] = 0x0;
|
|
else
|
|
gbMemory[temp] = 0x0f;
|
|
}
|
|
|
|
else
|
|
gbMemory[temp] = 0xff;
|
|
}
|
|
|
|
if(gbSpeed) {
|
|
gbSpeedSwitch();
|
|
gbMemory[0xff4d] = 0;
|
|
}
|
|
|
|
// GB bios set this memory area to 0
|
|
// Fixes Pitman (J) title screen
|
|
if (gbHardware & 0x1) {
|
|
memset(&gbMemory[0x8000], 0x0, 0x2000);
|
|
}
|
|
|
|
// clean LineBuffer
|
|
if (gbLineBuffer != NULL)
|
|
memset(gbLineBuffer, 0, sizeof(*gbLineBuffer));
|
|
// clean Pix
|
|
if (pix != NULL)
|
|
memset(pix, 0, sizeof(*pix));
|
|
// clean Vram
|
|
if (gbVram != NULL)
|
|
memset(gbVram, 0, 0x4000);
|
|
// clean Wram 2
|
|
// This kinda emulates the startup state of Wram on GBC (not very accurate,
|
|
// but way closer to the reality than filling it with 00es or FFes).
|
|
// On GBA/GBASP, it's kinda filled with random data.
|
|
// In all cases, most of the 2nd bank is filled with 00s.
|
|
// The starting data are important for some 'buggy' games, like Buster Brothers or
|
|
// Karamuchou ha Oosawagi!
|
|
if (gbWram != NULL)
|
|
{
|
|
for (int i = 0; i<8; i++)
|
|
if (i != 2)
|
|
memcpy ((u16 *)(gbWram+i*0x1000), (u16 *)(gbMemory+0xC000), 0x1000);
|
|
}
|
|
|
|
memset(gbSCYLine,0,sizeof(gbSCYLine));
|
|
memset(gbSCXLine,0,sizeof(gbSCXLine));
|
|
memset(gbBgpLine,0xfc,sizeof(gbBgpLine));
|
|
if (gbHardware & 5)
|
|
{
|
|
memset(gbObp0Line,0xff,sizeof(gbObp0Line));
|
|
memset(gbObp1Line,0xff,sizeof(gbObp1Line));
|
|
}
|
|
else
|
|
{
|
|
memset(gbObp0Line,0x0,sizeof(gbObp0Line));
|
|
memset(gbObp1Line,0x0,sizeof(gbObp1Line));
|
|
}
|
|
memset(gbSpritesTicks,0x0,sizeof(gbSpritesTicks));
|
|
|
|
SP.W = 0xfffe;
|
|
AF.W = 0x01b0;
|
|
BC.W = 0x0013;
|
|
DE.W = 0x00d8;
|
|
HL.W = 0x014d;
|
|
PC.W = 0x0100;
|
|
IFF = 0;
|
|
gbInt48Signal = 0;
|
|
|
|
register_TIMA = 0;
|
|
register_TMA = 0;
|
|
register_TAC = 0;
|
|
gbMemory[0xff0f] = register_IF = 1;
|
|
gbMemory[0xff40] = register_LCDC = 0x91;
|
|
gbMemory[0xff47] = 0xfc;
|
|
|
|
if (gbCgbMode)
|
|
gbMemory[0xff4d] = 0x7e;
|
|
else
|
|
gbMemory[0xff4d] = 0xff;
|
|
|
|
if (!gbCgbMode)
|
|
gbMemory[0xff70] = gbMemory[0xff74] = 0xff;
|
|
|
|
if (gbCgbMode)
|
|
gbMemory[0xff56] = 0x3e;
|
|
else
|
|
gbMemory[0xff56] = 0xff;
|
|
|
|
register_SCY = 0;
|
|
register_SCX = 0;
|
|
register_LYC = 0;
|
|
register_DMA = 0xff;
|
|
register_WY = 0;
|
|
register_WX = 0;
|
|
register_VBK = 0;
|
|
register_HDMA1 = 0xff;
|
|
register_HDMA2 = 0xff;
|
|
register_HDMA3 = 0xff;
|
|
register_HDMA4 = 0xff;
|
|
register_HDMA5 = 0xff;
|
|
register_SVBK = 0;
|
|
register_IE = 0;
|
|
|
|
if (gbCgbMode)
|
|
gbMemory[0xff02] = 0x7c;
|
|
else
|
|
gbMemory[0xff02] = 0x7e;
|
|
|
|
gbMemory[0xff03] = 0xff;
|
|
int i;
|
|
for (i = 0x8; i<0xf; i++)
|
|
gbMemory[0xff00+i] = 0xff;
|
|
|
|
gbMemory[0xff13] = 0xff;
|
|
gbMemory[0xff15] = 0xff;
|
|
gbMemory[0xff18] = 0xff;
|
|
gbMemory[0xff1d] = 0xff;
|
|
gbMemory[0xff1f] = 0xff;
|
|
|
|
for (i = 0x27; i<0x30; i++)
|
|
gbMemory[0xff00+i] = 0xff;
|
|
|
|
gbMemory[0xff4c] = 0xff;
|
|
gbMemory[0xff4e] = 0xff;
|
|
gbMemory[0xff50] = 0xff;
|
|
|
|
for (i = 0x57; i<0x68; i++)
|
|
gbMemory[0xff00+i] = 0xff;
|
|
|
|
for (i = 0x5d; i<0x70; i++)
|
|
gbMemory[0xff00+i] = 0xff;
|
|
|
|
gbMemory[0xff71] = 0xff;
|
|
|
|
for (i = 0x78; i<0x80; i++)
|
|
gbMemory[0xff00+i] = 0xff;
|
|
|
|
if (gbHardware & 0xa)
|
|
{
|
|
|
|
if (gbHardware & 2)
|
|
{
|
|
AF.W = 0x1180;
|
|
BC.W = 0x0000;
|
|
}
|
|
else
|
|
{
|
|
AF.W = 0x1100;
|
|
BC.W = 0x0100; // GBA/SP have B = 0x01 (which means GBC & GBA/SP bootrom are different !)
|
|
}
|
|
|
|
gbMemory[0xff26] = 0xf1;
|
|
if (gbCgbMode)
|
|
{
|
|
|
|
gbMemory[0xff31] = 0xff;
|
|
gbMemory[0xff33] = 0xff;
|
|
gbMemory[0xff35] = 0xff;
|
|
gbMemory[0xff37] = 0xff;
|
|
gbMemory[0xff39] = 0xff;
|
|
gbMemory[0xff3b] = 0xff;
|
|
gbMemory[0xff3d] = 0xff;
|
|
|
|
gbMemory[0xff44] = register_LY = 0x90;
|
|
gbDivTicks = 0x19 + ((gbHardware & 2) >> 1);
|
|
gbInternalTimer = 0x58 + ((gbHardware & 2) >> 1);
|
|
gbLcdTicks = GBLCD_MODE_1_CLOCK_TICKS -
|
|
(register_LY-0x8F)*GBLY_INCREMENT_CLOCK_TICKS + 72 + ((gbHardware & 2) >> 1);
|
|
gbLcdLYIncrementTicks = 72 + ((gbHardware & 2) >> 1);
|
|
gbMemory[0xff04] = register_DIV = 0x1E;
|
|
}
|
|
else
|
|
{
|
|
gbMemory[0xff44] = register_LY = 0x94;
|
|
gbDivTicks = 0x22 + ((gbHardware & 2) >> 1);
|
|
gbInternalTimer = 0x61 + ((gbHardware & 2) >> 1);
|
|
gbLcdTicks = GBLCD_MODE_1_CLOCK_TICKS -
|
|
(register_LY-0x8F)*GBLY_INCREMENT_CLOCK_TICKS + 25 + ((gbHardware & 2) >> 1);
|
|
gbLcdLYIncrementTicks = 25 + ((gbHardware & 2) >> 1);
|
|
gbMemory[0xff04] = register_DIV = 0x26;
|
|
}
|
|
|
|
|
|
DE.W = 0xff56;
|
|
HL.W = 0x000d;
|
|
|
|
register_HDMA5 = 0xff;
|
|
gbMemory[0xff68] = 0xc0;
|
|
gbMemory[0xff6a] = 0xc0;
|
|
|
|
|
|
gbMemory[0xff41] = register_STAT = 0x81;
|
|
gbLcdMode = 1;
|
|
}
|
|
else
|
|
{
|
|
if (gbHardware & 4)
|
|
{
|
|
if(gbEmulatorType == 5)
|
|
AF.W = 0xffb0;
|
|
else
|
|
AF.W = 0x01b0;
|
|
BC.W = 0x0013;
|
|
DE.W = 0x00d8;
|
|
HL.W = 0x014d;
|
|
}
|
|
gbDivTicks = 14;
|
|
gbInternalTimer = gbDivTicks--;
|
|
gbMemory[0xff04] = register_DIV = 0xAB;
|
|
gbMemory[0xff41] = register_STAT = 0x85;
|
|
gbMemory[0xff44] = register_LY = 0x00;
|
|
gbLcdTicks = 15;
|
|
gbLcdLYIncrementTicks = 114+gbLcdTicks;
|
|
gbLcdMode = 1;
|
|
|
|
// used for the handling of the gb Boot Rom
|
|
if ((gbHardware & 5) && (bios != NULL) && useBios && !skipBios)
|
|
{
|
|
memcpy ((u8 *)(gbMemory), (u8 *)(gbRom), 0x1000);
|
|
memcpy ((u8 *)(gbMemory), (u8 *)(bios), 0x100);
|
|
gbWhiteScreen = 0;
|
|
|
|
gbInternalTimer = 0x3e;
|
|
gbDivTicks = 0x3f;
|
|
gbMemory[0xff04] = register_DIV = 0x00;
|
|
PC.W = 0x0000;
|
|
register_LCDC = 0x11;
|
|
gbScreenOn = false;
|
|
gbLcdTicks = 0;
|
|
gbLcdMode = 0;
|
|
gbLcdModeDelayed = 0;
|
|
gbMemory[0xff41] = register_STAT &= 0xfc;
|
|
gbInt48Signal = 0;
|
|
gbLcdLYIncrementTicks = GBLY_INCREMENT_CLOCK_TICKS;
|
|
}
|
|
}
|
|
|
|
gbLine99Ticks = 1;
|
|
if (gbHardware & 8)
|
|
gbLine99Ticks++;
|
|
|
|
gbLcdModeDelayed = gbLcdMode;
|
|
gbLcdTicksDelayed = gbLcdTicks+1;
|
|
gbLcdLYIncrementTicksDelayed = gbLcdLYIncrementTicks+1;
|
|
|
|
|
|
gbTimerModeChange = false;
|
|
gbTimerOnChange = false;
|
|
gbTimerOn = false;
|
|
|
|
if(gbCgbMode) {
|
|
for (int i = 0; i<0x20; i++)
|
|
gbPalette[i] = 0x7fff;
|
|
|
|
// This is just to show that the starting values of the OBJ palettes are different
|
|
// between the 3 consoles, and that they 'kinda' stay the same at each reset
|
|
// (they can slightly change, somehow (randomly?)).
|
|
// You can check the effects of gbGBCColorType on the "Vila Caldan Color" gbc demo.
|
|
// Note that you could also check the Div register to check on which system the game
|
|
// is running (GB,GBC and GBA(SP) have different startup values).
|
|
// Unfortunatly, I don't have any SGB system, so I can't get their starting values.
|
|
|
|
if (gbGBCColorType == 0) // GBC Hardware
|
|
{
|
|
gbPalette[0x20] = 0x0600;
|
|
gbPalette[0x21] = 0xfdf3;
|
|
gbPalette[0x22] = 0x041c;
|
|
gbPalette[0x23] = 0xf5db;
|
|
gbPalette[0x24] = 0x4419;
|
|
gbPalette[0x25] = 0x57ea;
|
|
gbPalette[0x26] = 0x2808;
|
|
gbPalette[0x27] = 0x9b75;
|
|
gbPalette[0x28] = 0x129b;
|
|
gbPalette[0x29] = 0xfce0;
|
|
gbPalette[0x2a] = 0x22da;
|
|
gbPalette[0x2b] = 0x4ac5;
|
|
gbPalette[0x2c] = 0x2d71;
|
|
gbPalette[0x2d] = 0xf0c2;
|
|
gbPalette[0x2e] = 0x5137;
|
|
gbPalette[0x2f] = 0x2d41;
|
|
gbPalette[0x30] = 0x6b2d;
|
|
gbPalette[0x31] = 0x2215;
|
|
gbPalette[0x32] = 0xbe0a;
|
|
gbPalette[0x33] = 0xc053;
|
|
gbPalette[0x34] = 0xfe5f;
|
|
gbPalette[0x35] = 0xe000;
|
|
gbPalette[0x36] = 0xbe10;
|
|
gbPalette[0x37] = 0x914d;
|
|
gbPalette[0x38] = 0x7f91;
|
|
gbPalette[0x39] = 0x02b5;
|
|
gbPalette[0x3a] = 0x77ac;
|
|
gbPalette[0x3b] = 0x14e5;
|
|
gbPalette[0x3c] = 0xcf89;
|
|
gbPalette[0x3d] = 0xa03d;
|
|
gbPalette[0x3e] = 0xfd50;
|
|
gbPalette[0x3f] = 0x91ff;
|
|
}
|
|
else if (gbGBCColorType == 1) // GBA Hardware
|
|
{
|
|
gbPalette[0x20] = 0xbe00;
|
|
gbPalette[0x21] = 0xfdfd;
|
|
gbPalette[0x22] = 0xbd69;
|
|
gbPalette[0x23] = 0x7baf;
|
|
gbPalette[0x24] = 0xf5ff;
|
|
gbPalette[0x25] = 0x3f8f;
|
|
gbPalette[0x26] = 0xcee5;
|
|
gbPalette[0x27] = 0x5bf7;
|
|
gbPalette[0x28] = 0xb35b;
|
|
gbPalette[0x29] = 0xef97;
|
|
gbPalette[0x2a] = 0xef9f;
|
|
gbPalette[0x2b] = 0x97f7;
|
|
gbPalette[0x2c] = 0x82bf;
|
|
gbPalette[0x2d] = 0x9f3d;
|
|
gbPalette[0x2e] = 0xddde;
|
|
gbPalette[0x2f] = 0xbad5;
|
|
gbPalette[0x30] = 0x3cba;
|
|
gbPalette[0x31] = 0xdfd7;
|
|
gbPalette[0x32] = 0xedea;
|
|
gbPalette[0x33] = 0xfeda;
|
|
gbPalette[0x34] = 0xf7f9;
|
|
gbPalette[0x35] = 0xfdee;
|
|
gbPalette[0x36] = 0x6d2f;
|
|
gbPalette[0x37] = 0xf0e6;
|
|
gbPalette[0x38] = 0xf7f0;
|
|
gbPalette[0x39] = 0xf296;
|
|
gbPalette[0x3a] = 0x3bf1;
|
|
gbPalette[0x3b] = 0xe211;
|
|
gbPalette[0x3c] = 0x69ba;
|
|
gbPalette[0x3d] = 0x3d0d;
|
|
gbPalette[0x3e] = 0xdfd3;
|
|
gbPalette[0x3f] = 0xa6ba;
|
|
}
|
|
else if (gbGBCColorType == 2) // GBASP Hardware
|
|
{
|
|
gbPalette[0x20] = 0x9c00;
|
|
gbPalette[0x21] = 0x6340;
|
|
gbPalette[0x22] = 0x10c6;
|
|
gbPalette[0x23] = 0xdb97;
|
|
gbPalette[0x24] = 0x7622;
|
|
gbPalette[0x25] = 0x3e57;
|
|
gbPalette[0x26] = 0x2e12;
|
|
gbPalette[0x27] = 0x95c3;
|
|
gbPalette[0x28] = 0x1095;
|
|
gbPalette[0x29] = 0x488c;
|
|
gbPalette[0x2a] = 0x8241;
|
|
gbPalette[0x2b] = 0xde8c;
|
|
gbPalette[0x2c] = 0xfabc;
|
|
gbPalette[0x2d] = 0x0e81;
|
|
gbPalette[0x2e] = 0x7675;
|
|
gbPalette[0x2f] = 0xfdec;
|
|
gbPalette[0x30] = 0xddfd;
|
|
gbPalette[0x31] = 0x5995;
|
|
gbPalette[0x32] = 0x066a;
|
|
gbPalette[0x33] = 0xed1e;
|
|
gbPalette[0x34] = 0x1e84;
|
|
gbPalette[0x35] = 0x1d14;
|
|
gbPalette[0x36] = 0x11c3;
|
|
gbPalette[0x37] = 0x2749;
|
|
gbPalette[0x38] = 0xa727;
|
|
gbPalette[0x39] = 0x6266;
|
|
gbPalette[0x3a] = 0xe27b;
|
|
gbPalette[0x3b] = 0xe3fc;
|
|
gbPalette[0x3c] = 0x1f76;
|
|
gbPalette[0x3d] = 0xf158;
|
|
gbPalette[0x3e] = 0x468e;
|
|
gbPalette[0x3f] = 0xa540;
|
|
}
|
|
} else {
|
|
if(gbSgbMode) {
|
|
for(int i = 0; i < 12; i++)
|
|
gbPalette[i] = systemGbPalette[gbPaletteOption*12+i];
|
|
|
|
}
|
|
for(int i = 0; i < 12; i++)
|
|
gbPalette[i] = systemGbPalette[gbPaletteOption*12+i];
|
|
}
|
|
|
|
GBTIMER_MODE_0_CLOCK_TICKS = 256;
|
|
GBTIMER_MODE_1_CLOCK_TICKS = 4;
|
|
GBTIMER_MODE_2_CLOCK_TICKS = 16;
|
|
GBTIMER_MODE_3_CLOCK_TICKS = 64;
|
|
|
|
GBLY_INCREMENT_CLOCK_TICKS = 114;
|
|
gbTimerTicks = GBTIMER_MODE_0_CLOCK_TICKS;
|
|
gbTimerClockTicks = GBTIMER_MODE_0_CLOCK_TICKS;
|
|
gbSerialTicks = 0;
|
|
gbSerialBits = 0;
|
|
gbSerialOn = 0;
|
|
gbWindowLine = -1;
|
|
gbTimerOn = false;
|
|
gbTimerMode = 0;
|
|
gbSpeed = 0;
|
|
gbJoymask[0] = gbJoymask[1] = gbJoymask[2] = gbJoymask[3] = 0;
|
|
|
|
if(gbCgbMode) {
|
|
gbSpeed = 0;
|
|
gbHdmaOn = 0;
|
|
gbHdmaSource = 0x99d0;
|
|
gbHdmaDestination = 0x99d0;
|
|
gbVramBank = 0;
|
|
gbWramBank = 1;
|
|
|
|
}
|
|
|
|
// used to clean the borders
|
|
if (gbSgbMode)
|
|
{
|
|
gbSgbResetFlag = true;
|
|
gbSgbReset();
|
|
if (gbBorderOn)
|
|
gbSgbRenderBorder();
|
|
gbSgbResetFlag = false;
|
|
}
|
|
|
|
for(i = 0; i < 4; i++)
|
|
gbBgp[i] = gbObp0[i] = gbObp1[i] = i;
|
|
|
|
memset(&gbDataMBC1,0, sizeof(gbDataMBC1));
|
|
gbDataMBC1.mapperROMBank = 1;
|
|
|
|
gbDataMBC2.mapperRAMEnable = 0;
|
|
gbDataMBC2.mapperROMBank = 1;
|
|
|
|
memset(&gbDataMBC3,0, 6 * sizeof(int));
|
|
gbDataMBC3.mapperROMBank = 1;
|
|
|
|
memset(&gbDataMBC5, 0, sizeof(gbDataMBC5));
|
|
gbDataMBC5.mapperROMBank = 1;
|
|
if (gbRomType >= 0x1c && gbRomType<=0x1e)
|
|
gbDataMBC5.isRumbleCartridge = 1;
|
|
|
|
memset(&gbDataMBC7, 0, sizeof(gbDataMBC7));
|
|
gbDataMBC7.mapperROMBank = 1;
|
|
|
|
memset(&gbDataHuC1, 0, sizeof(gbDataHuC1));
|
|
gbDataHuC1.mapperROMBank = 1;
|
|
|
|
memset(&gbDataHuC3, 0, sizeof(gbDataHuC3));
|
|
gbDataHuC3.mapperROMBank = 1;
|
|
|
|
memset(&gbDataTAMA5,0, 26*sizeof(int));
|
|
gbDataTAMA5.mapperROMBank = 1;
|
|
|
|
memset(&gbDataMMM01,0, sizeof(gbDataMMM01));
|
|
gbDataMMM01.mapperROMBank = 1;
|
|
|
|
if (useBios && !skipBios && (gbHardware & 5))
|
|
{
|
|
gbMemoryMap[0x00] = &gbMemory[0x0000];
|
|
inBios = true;
|
|
}
|
|
else
|
|
{
|
|
gbMemoryMap[0x00] = &gbRom[0x0000];
|
|
inBios = false;
|
|
}
|
|
|
|
gbMemoryMap[0x01] = &gbRom[0x1000];
|
|
gbMemoryMap[0x02] = &gbRom[0x2000];
|
|
gbMemoryMap[0x03] = &gbRom[0x3000];
|
|
gbMemoryMap[0x04] = &gbRom[0x4000];
|
|
gbMemoryMap[0x05] = &gbRom[0x5000];
|
|
gbMemoryMap[0x06] = &gbRom[0x6000];
|
|
gbMemoryMap[0x07] = &gbRom[0x7000];
|
|
if(gbCgbMode) {
|
|
gbMemoryMap[0x08] = &gbVram[0x0000];
|
|
gbMemoryMap[0x09] = &gbVram[0x1000];
|
|
gbMemoryMap[0x0a] = &gbMemory[0xa000];
|
|
gbMemoryMap[0x0b] = &gbMemory[0xb000];
|
|
gbMemoryMap[0x0c] = &gbMemory[0xc000];
|
|
gbMemoryMap[0x0d] = &gbWram[0x1000];
|
|
gbMemoryMap[0x0e] = &gbMemory[0xe000];
|
|
gbMemoryMap[0x0f] = &gbMemory[0xf000];
|
|
} else {
|
|
gbMemoryMap[0x08] = &gbMemory[0x8000];
|
|
gbMemoryMap[0x09] = &gbMemory[0x9000];
|
|
gbMemoryMap[0x0a] = &gbMemory[0xa000];
|
|
gbMemoryMap[0x0b] = &gbMemory[0xb000];
|
|
gbMemoryMap[0x0c] = &gbMemory[0xc000];
|
|
gbMemoryMap[0x0d] = &gbMemory[0xd000];
|
|
gbMemoryMap[0x0e] = &gbMemory[0xe000];
|
|
gbMemoryMap[0x0f] = &gbMemory[0xf000];
|
|
}
|
|
|
|
if(gbRam) {
|
|
gbMemoryMap[0x0a] = &gbRam[0x0000];
|
|
gbMemoryMap[0x0b] = &gbRam[0x1000];
|
|
}
|
|
|
|
gbSoundReset();
|
|
|
|
systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED;
|
|
|
|
gbLastTime = systemGetClock();
|
|
gbFrameCount = 0;
|
|
|
|
gbScreenOn = true;
|
|
gbSystemMessage = false;
|
|
|
|
gbCheatWrite(true); // Emulates GS codes.
|
|
|
|
}
|
|
|
|
void gbWriteSaveMBC1(const char * name)
|
|
{
|
|
if (gbRam)
|
|
{
|
|
FILE *gzFile = fopen(name,"wb");
|
|
|
|
if(gzFile == NULL) {
|
|
systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name);
|
|
return;
|
|
}
|
|
|
|
fwrite(gbRam,
|
|
1,
|
|
(gbRamSizeMask+1),
|
|
gzFile);
|
|
|
|
fclose(gzFile);
|
|
}
|
|
}
|
|
|
|
void gbWriteSaveMBC2(const char * name)
|
|
{
|
|
if (gbRam)
|
|
{
|
|
FILE *file = fopen(name, "wb");
|
|
|
|
if(file == NULL) {
|
|
systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name);
|
|
return;
|
|
}
|
|
|
|
fwrite(gbRam,
|
|
1,
|
|
512,
|
|
file);
|
|
|
|
fclose(file);
|
|
}
|
|
}
|
|
|
|
void gbWriteSaveMBC3(const char * name, bool extendedSave)
|
|
{
|
|
if (gbRam || extendedSave)
|
|
{
|
|
FILE *gzFile = fopen(name,"wb");
|
|
if (gbRam)
|
|
{
|
|
|
|
if(gzFile == NULL) {
|
|
systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name);
|
|
return;
|
|
}
|
|
|
|
fwrite(gbRam,
|
|
1,
|
|
(gbRamSizeMask+1),
|
|
gzFile);
|
|
}
|
|
|
|
if(extendedSave)
|
|
fwrite(&gbDataMBC3.mapperSeconds,
|
|
1,
|
|
10*sizeof(int) + sizeof(time_t),
|
|
gzFile);
|
|
|
|
fclose(gzFile);
|
|
}
|
|
}
|
|
|
|
void gbWriteSaveMBC5(const char * name)
|
|
{
|
|
if (gbRam)
|
|
{
|
|
FILE *gzFile = fopen(name,"wb");
|
|
|
|
if(gzFile == NULL) {
|
|
systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name);
|
|
return;
|
|
}
|
|
|
|
fwrite(gbRam,
|
|
1,
|
|
(gbRamSizeMask+1),
|
|
gzFile);
|
|
|
|
fclose(gzFile);
|
|
}
|
|
}
|
|
|
|
void gbWriteSaveMBC7(const char * name)
|
|
{
|
|
if (gbRam)
|
|
{
|
|
FILE *file = fopen(name, "wb");
|
|
|
|
if(file == NULL) {
|
|
systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name);
|
|
return;
|
|
}
|
|
|
|
fwrite(gbRam,
|
|
1,
|
|
256,
|
|
file);
|
|
|
|
fclose(file);
|
|
}
|
|
}
|
|
|
|
void gbWriteSaveTAMA5(const char * name, bool extendedSave)
|
|
{
|
|
FILE *gzFile = fopen(name,"wb");
|
|
|
|
if(gzFile == NULL) {
|
|
systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name);
|
|
return;
|
|
}
|
|
if (gbRam)
|
|
fwrite(gbRam,
|
|
1,
|
|
(gbRamSizeMask+1),
|
|
gzFile);
|
|
|
|
fwrite(gbTAMA5ram,
|
|
1,
|
|
(gbTAMA5ramSize),
|
|
gzFile);
|
|
|
|
if(extendedSave)
|
|
fwrite(&gbDataTAMA5.mapperSeconds,
|
|
1,
|
|
14*sizeof(int) + sizeof(time_t),
|
|
gzFile);
|
|
|
|
fclose(gzFile);
|
|
}
|
|
|
|
void gbWriteSaveMMM01(const char * name)
|
|
{
|
|
if (gbRam)
|
|
{
|
|
FILE *gzFile = fopen(name,"wb");
|
|
|
|
if(gzFile == NULL) {
|
|
systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name);
|
|
return;
|
|
}
|
|
|
|
fwrite(gbRam,
|
|
1,
|
|
(gbRamSizeMask+1),
|
|
gzFile);
|
|
|
|
fclose(gzFile);
|
|
}
|
|
}
|
|
|
|
|
|
bool gbReadSaveMBC1(const char * name)
|
|
{
|
|
if (gbRam)
|
|
{
|
|
gzFile gzFile = gzopen(name, "rb");
|
|
|
|
if(gzFile == NULL) {
|
|
return false;
|
|
}
|
|
|
|
int read = gzread(gzFile,
|
|
gbRam,
|
|
(gbRamSizeMask+1));
|
|
|
|
if(read != (gbRamSizeMask+1)) {
|
|
systemMessage(MSG_FAILED_TO_READ_SGM,
|
|
N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read);
|
|
gzclose(gzFile);
|
|
gbBatteryError = true;
|
|
return false;
|
|
}
|
|
|
|
// Also checks if the battery file it bigger than gbRamSizeMask+1 !
|
|
u8 data[1];
|
|
data[0] = 0;
|
|
|
|
read = gzread(gzFile,
|
|
data,
|
|
1);
|
|
if(read >0) {
|
|
systemMessage(MSG_FAILED_TO_READ_SGM,
|
|
N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read);
|
|
gzclose(gzFile);
|
|
gbBatteryError = true;
|
|
return false;
|
|
}
|
|
|
|
gzclose(gzFile);
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
|
|
bool gbReadSaveMBC2(const char * name)
|
|
{
|
|
if (gbRam)
|
|
{
|
|
FILE *file = fopen(name, "rb");
|
|
|
|
if(file == NULL) {
|
|
return false;
|
|
}
|
|
|
|
size_t read = fread(gbRam,
|
|
1,
|
|
512,
|
|
file);
|
|
|
|
if(read != 512) {
|
|
systemMessage(MSG_FAILED_TO_READ_SGM,
|
|
N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read);
|
|
fclose(file);
|
|
gbBatteryError = true;
|
|
return false;
|
|
}
|
|
|
|
// Also checks if the battery file it bigger than gbRamSizeMask+1 !
|
|
u8 data[1];
|
|
data[0] = 0;
|
|
|
|
read = fread(&data[0],
|
|
1,
|
|
1,
|
|
file);
|
|
if(read > 0) {
|
|
systemMessage(MSG_FAILED_TO_READ_SGM,
|
|
N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read);
|
|
fclose(file);
|
|
gbBatteryError = true;
|
|
return false;
|
|
}
|
|
|
|
fclose(file);
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
bool gbReadSaveMBC3(const char * name)
|
|
{
|
|
gzFile gzFile = gzopen(name, "rb");
|
|
|
|
if(gzFile == NULL) {
|
|
return false;
|
|
}
|
|
|
|
int read = 0;
|
|
|
|
if (gbRam)
|
|
read = gzread(gzFile,
|
|
gbRam,
|
|
(gbRamSizeMask+1));
|
|
else
|
|
read = (gbRamSizeMask+1);
|
|
|
|
|
|
bool res = true;
|
|
|
|
if(read != (gbRamSizeMask+1)) {
|
|
systemMessage(MSG_FAILED_TO_READ_SGM,
|
|
N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read);
|
|
gbBatteryError = true;
|
|
res = false;
|
|
} else if ((gbRomType == 0xf) || (gbRomType == 0x10)){
|
|
read = gzread(gzFile,
|
|
&gbDataMBC3.mapperSeconds,
|
|
sizeof(int)*10 + sizeof(time_t));
|
|
|
|
if(read != (sizeof(int)*10 + sizeof(time_t)) && read != 0) {
|
|
systemMessage(MSG_FAILED_TO_READ_RTC,N_("Failed to read RTC from save game %s (continuing)"),
|
|
name);
|
|
res = false;
|
|
}
|
|
else if (read == 0)
|
|
{
|
|
systemMessage(MSG_FAILED_TO_READ_RTC,N_("Failed to read RTC from save game %s (continuing)"),
|
|
name);
|
|
res = false;
|
|
}
|
|
else
|
|
{
|
|
// Also checks if the battery file it bigger than gbRamSizeMask+1+RTC !
|
|
u8 data[1];
|
|
data[0] = 0;
|
|
|
|
read = gzread(gzFile,
|
|
data,
|
|
1);
|
|
if(read >0) {
|
|
systemMessage(MSG_FAILED_TO_READ_SGM,
|
|
N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read);
|
|
gbBatteryError = true;
|
|
res = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
gzclose(gzFile);
|
|
return res;
|
|
}
|
|
|
|
bool gbReadSaveMBC5(const char * name)
|
|
{
|
|
if (gbRam)
|
|
{
|
|
gzFile gzFile = gzopen(name, "rb");
|
|
|
|
if(gzFile == NULL) {
|
|
return false;
|
|
}
|
|
|
|
int read = gzread(gzFile,
|
|
gbRam,
|
|
(gbRamSizeMask+1));
|
|
|
|
if(read != (gbRamSizeMask+1)) {
|
|
systemMessage(MSG_FAILED_TO_READ_SGM,
|
|
N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read);
|
|
gzclose(gzFile);
|
|
gbBatteryError = true;
|
|
return false;
|
|
}
|
|
|
|
|
|
// Also checks if the battery file it bigger than gbRamSizeMask+1 !
|
|
u8 data[1];
|
|
data[0] = 0;
|
|
|
|
read = gzread(gzFile,
|
|
data,
|
|
1);
|
|
if(read >0) {
|
|
systemMessage(MSG_FAILED_TO_READ_SGM,
|
|
N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read);
|
|
gzclose(gzFile);
|
|
gbBatteryError = true;
|
|
return false;
|
|
}
|
|
|
|
gzclose(gzFile);
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
bool gbReadSaveMBC7(const char * name)
|
|
{
|
|
if (gbRam)
|
|
{
|
|
FILE *file = fopen(name, "rb");
|
|
|
|
if(file == NULL) {
|
|
return false;
|
|
}
|
|
|
|
size_t read = fread(gbRam,
|
|
1,
|
|
256,
|
|
file);
|
|
|
|
if(read != 256) {
|
|
systemMessage(MSG_FAILED_TO_READ_SGM,
|
|
N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read);
|
|
fclose(file);
|
|
gbBatteryError = true;
|
|
return false;
|
|
}
|
|
|
|
// Also checks if the battery file it bigger than gbRamSizeMask+1 !
|
|
u8 data[1];
|
|
data[0] = 0;
|
|
|
|
read = fread(&data[0],
|
|
1,
|
|
1,
|
|
file);
|
|
if(read > 0) {
|
|
systemMessage(MSG_FAILED_TO_READ_SGM,
|
|
N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read);
|
|
fclose(file);
|
|
gbBatteryError = true;
|
|
return false;
|
|
}
|
|
|
|
fclose(file);
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
bool gbReadSaveTAMA5(const char * name)
|
|
{
|
|
gzFile gzFile = gzopen(name, "rb");
|
|
|
|
if(gzFile == NULL) {
|
|
return false;
|
|
}
|
|
|
|
int read = 0;
|
|
|
|
if (gbRam)
|
|
read = gzread(gzFile,
|
|
gbRam,
|
|
(gbRamSizeMask+1));
|
|
else
|
|
read = gbRamSizeMask;
|
|
|
|
read += gzread(gzFile,
|
|
gbTAMA5ram,
|
|
gbTAMA5ramSize);
|
|
|
|
bool res = true;
|
|
|
|
if(read != (gbRamSizeMask+gbTAMA5ramSize+1)) {
|
|
systemMessage(MSG_FAILED_TO_READ_SGM,
|
|
N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read);
|
|
gbBatteryError = true;
|
|
res = false;
|
|
} else {
|
|
read = gzread(gzFile,
|
|
&gbDataTAMA5.mapperSeconds,
|
|
sizeof(int)*14 + sizeof(time_t));
|
|
|
|
if(read != (sizeof(int)*14 + sizeof(time_t)) && read != 0) {
|
|
systemMessage(MSG_FAILED_TO_READ_RTC,N_("Failed to read RTC from save game %s (continuing)"),
|
|
name);
|
|
res = false;
|
|
}
|
|
else if (read == 0)
|
|
{
|
|
systemMessage(MSG_FAILED_TO_READ_RTC,N_("Failed to read RTC from save game %s (continuing)"),
|
|
name);
|
|
res = false;
|
|
}
|
|
else
|
|
{
|
|
// Also checks if the battery file it bigger than gbRamSizeMask+1+RTC !
|
|
u8 data[1];
|
|
data[0] = 0;
|
|
|
|
read = gzread(gzFile,
|
|
data,
|
|
1);
|
|
if(read >0) {
|
|
systemMessage(MSG_FAILED_TO_READ_SGM,
|
|
N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read);
|
|
gbBatteryError = true;
|
|
res = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
gzclose(gzFile);
|
|
return res;
|
|
}
|
|
|
|
|
|
bool gbReadSaveMMM01(const char * name)
|
|
{
|
|
if (gbRam)
|
|
{
|
|
gzFile gzFile = gzopen(name, "rb");
|
|
|
|
if(gzFile == NULL) {
|
|
return false;
|
|
}
|
|
|
|
int read = gzread(gzFile,
|
|
gbRam,
|
|
(gbRamSizeMask+1));
|
|
|
|
if(read != (gbRamSizeMask+1)) {
|
|
systemMessage(MSG_FAILED_TO_READ_SGM,
|
|
N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read);
|
|
gzclose(gzFile);
|
|
gbBatteryError = true;
|
|
return false;
|
|
}
|
|
|
|
// Also checks if the battery file it bigger than gbRamSizeMask+1 !
|
|
u8 data[1];
|
|
data[0] = 0;
|
|
|
|
read = gzread(gzFile,
|
|
data,
|
|
1);
|
|
if(read >0) {
|
|
systemMessage(MSG_FAILED_TO_READ_SGM,
|
|
N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read);
|
|
gzclose(gzFile);
|
|
gbBatteryError = true;
|
|
return false;
|
|
}
|
|
|
|
gzclose(gzFile);
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
void gbInit()
|
|
{
|
|
gbGenFilter();
|
|
gbSgbInit();
|
|
|
|
gbMemory = (u8 *)malloc(65536);
|
|
|
|
pix = (u8 *)calloc(1,4*257*226);
|
|
|
|
gbLineBuffer = (u16 *)malloc(160 * sizeof(u16));
|
|
}
|
|
|
|
bool gbWriteBatteryFile(const char *file, bool extendedSave)
|
|
{
|
|
if(gbBattery) {
|
|
switch(gbRomType) {
|
|
case 0x03:
|
|
gbWriteSaveMBC1(file);
|
|
break;
|
|
case 0x06:
|
|
gbWriteSaveMBC2(file);
|
|
break;
|
|
case 0x0d:
|
|
gbWriteSaveMMM01(file);
|
|
break;
|
|
case 0x0f:
|
|
case 0x10:
|
|
gbWriteSaveMBC3(file, extendedSave);
|
|
break;
|
|
case 0x13:
|
|
case 0xfc:
|
|
gbWriteSaveMBC3(file, false);
|
|
case 0x1b:
|
|
case 0x1e:
|
|
gbWriteSaveMBC5(file);
|
|
break;
|
|
case 0x22:
|
|
gbWriteSaveMBC7(file);
|
|
break;
|
|
case 0xfd:
|
|
gbWriteSaveTAMA5(file, extendedSave);
|
|
break;
|
|
case 0xff:
|
|
gbWriteSaveMBC1(file);
|
|
break;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool gbWriteBatteryFile(const char *file)
|
|
{
|
|
if (!gbBatteryError)
|
|
{
|
|
gbWriteBatteryFile(file, true);
|
|
return true;
|
|
}
|
|
else return false;
|
|
}
|
|
|
|
bool gbReadBatteryFile(const char *file)
|
|
{
|
|
bool res = false;
|
|
if(gbBattery) {
|
|
switch(gbRomType) {
|
|
case 0x03:
|
|
res = gbReadSaveMBC1(file);
|
|
break;
|
|
case 0x06:
|
|
res = gbReadSaveMBC2(file);
|
|
break;
|
|
case 0x0d:
|
|
res = gbReadSaveMMM01(file);
|
|
break;
|
|
case 0x0f:
|
|
case 0x10:
|
|
if(!gbReadSaveMBC3(file)) {
|
|
time(&gbDataMBC3.mapperLastTime);
|
|
struct tm *lt;
|
|
lt = localtime(&gbDataMBC3.mapperLastTime);
|
|
gbDataMBC3.mapperSeconds = lt->tm_sec;
|
|
gbDataMBC3.mapperMinutes = lt->tm_min;
|
|
gbDataMBC3.mapperHours = lt->tm_hour;
|
|
gbDataMBC3.mapperDays = lt->tm_yday & 255;
|
|
gbDataMBC3.mapperControl = (gbDataMBC3.mapperControl & 0xfe) |
|
|
(lt->tm_yday > 255 ? 1: 0);
|
|
res = false;
|
|
break;
|
|
}
|
|
res = true;
|
|
break;
|
|
case 0x13:
|
|
case 0xfc:
|
|
res = gbReadSaveMBC3(file);
|
|
break;
|
|
case 0x1b:
|
|
case 0x1e:
|
|
res = gbReadSaveMBC5(file);
|
|
break;
|
|
case 0x22:
|
|
res = gbReadSaveMBC7(file);
|
|
break;
|
|
case 0xfd:
|
|
if(!gbReadSaveTAMA5(file)) {
|
|
u8 gbDaysinMonth [12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
|
time(&gbDataTAMA5.mapperLastTime);
|
|
struct tm *lt;
|
|
lt = localtime(&gbDataTAMA5.mapperLastTime);
|
|
gbDataTAMA5.mapperSeconds = lt->tm_sec;
|
|
gbDataTAMA5.mapperMinutes = lt->tm_min;
|
|
gbDataTAMA5.mapperHours = lt->tm_hour;
|
|
gbDataTAMA5.mapperDays = 1;
|
|
gbDataTAMA5.mapperMonths = 1;
|
|
gbDataTAMA5.mapperYears = 1970;
|
|
int days = lt->tm_yday+365*3;
|
|
while (days)
|
|
{
|
|
gbDataTAMA5.mapperDays++;
|
|
days--;
|
|
if (gbDataTAMA5.mapperDays>gbDaysinMonth[gbDataTAMA5.mapperMonths-1])
|
|
{
|
|
gbDataTAMA5.mapperDays = 1;
|
|
gbDataTAMA5.mapperMonths++;
|
|
if (gbDataTAMA5.mapperMonths>12)
|
|
{
|
|
gbDataTAMA5.mapperMonths = 1;
|
|
gbDataTAMA5.mapperYears++;
|
|
if ((gbDataTAMA5.mapperYears & 3) == 0)
|
|
gbDaysinMonth[1] = 29;
|
|
else
|
|
gbDaysinMonth[1] = 28;
|
|
}
|
|
}
|
|
}
|
|
gbDataTAMA5.mapperControl = (gbDataTAMA5.mapperControl & 0xfe) |
|
|
(lt->tm_yday > 255 ? 1: 0);
|
|
res = false;
|
|
break;
|
|
}
|
|
res = true;
|
|
break;
|
|
case 0xff:
|
|
res = gbReadSaveMBC1(file);
|
|
break;
|
|
}
|
|
}
|
|
systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED;
|
|
return res;
|
|
}
|
|
|
|
bool gbReadGSASnapshot(const char *fileName)
|
|
{
|
|
FILE *file = fopen(fileName, "rb");
|
|
|
|
if(!file) {
|
|
systemMessage(MSG_CANNOT_OPEN_FILE, N_("Cannot open file %s"), fileName);
|
|
return false;
|
|
}
|
|
|
|
fseek(file, 0x4, SEEK_SET);
|
|
char buffer[16];
|
|
char buffer2[16];
|
|
fread(buffer, 1, 15, file);
|
|
buffer[15] = 0;
|
|
memcpy(buffer2, &gbRom[0x134], 15);
|
|
buffer2[15] = 0;
|
|
if(memcmp(buffer, buffer2, 15)) {
|
|
systemMessage(MSG_CANNOT_IMPORT_SNAPSHOT_FOR,
|
|
N_("Cannot import snapshot for %s. Current game is %s"),
|
|
buffer,
|
|
buffer2);
|
|
fclose(file);
|
|
return false;
|
|
}
|
|
fseek(file, 0x13, SEEK_SET);
|
|
size_t read = 0;
|
|
int toRead = 0;
|
|
switch(gbRomType) {
|
|
case 0x03:
|
|
case 0x0f:
|
|
case 0x10:
|
|
case 0x13:
|
|
case 0x1b:
|
|
case 0x1e:
|
|
case 0xff:
|
|
read = fread(gbRam, 1, (gbRamSizeMask+1), file);
|
|
toRead = (gbRamSizeMask+1);
|
|
break;
|
|
case 0x06:
|
|
case 0x22:
|
|
read = fread(&gbMemory[0xa000],1,256,file);
|
|
toRead = 256;
|
|
break;
|
|
default:
|
|
systemMessage(MSG_UNSUPPORTED_SNAPSHOT_FILE,
|
|
N_("Unsupported snapshot file %s"),
|
|
fileName);
|
|
fclose(file);
|
|
return false;
|
|
}
|
|
fclose(file);
|
|
gbReset();
|
|
return true;
|
|
}
|
|
|
|
variable_desc gbSaveGameStruct[] = {
|
|
{ &PC.W, sizeof(u16) },
|
|
{ &SP.W, sizeof(u16) },
|
|
{ &AF.W, sizeof(u16) },
|
|
{ &BC.W, sizeof(u16) },
|
|
{ &DE.W, sizeof(u16) },
|
|
{ &HL.W, sizeof(u16) },
|
|
{ &IFF, sizeof(u8) },
|
|
{ &GBLCD_MODE_0_CLOCK_TICKS, sizeof(int) },
|
|
{ &GBLCD_MODE_1_CLOCK_TICKS, sizeof(int) },
|
|
{ &GBLCD_MODE_2_CLOCK_TICKS, sizeof(int) },
|
|
{ &GBLCD_MODE_3_CLOCK_TICKS, sizeof(int) },
|
|
{ &GBDIV_CLOCK_TICKS, sizeof(int) },
|
|
{ &GBLY_INCREMENT_CLOCK_TICKS, sizeof(int) },
|
|
{ &GBTIMER_MODE_0_CLOCK_TICKS, sizeof(int) },
|
|
{ &GBTIMER_MODE_1_CLOCK_TICKS, sizeof(int) },
|
|
{ &GBTIMER_MODE_2_CLOCK_TICKS, sizeof(int) },
|
|
{ &GBTIMER_MODE_3_CLOCK_TICKS, sizeof(int) },
|
|
{ &GBSERIAL_CLOCK_TICKS, sizeof(int) },
|
|
{ &GBSYNCHRONIZE_CLOCK_TICKS, sizeof(int) },
|
|
{ &gbDivTicks, sizeof(int) },
|
|
{ &gbLcdMode, sizeof(int) },
|
|
{ &gbLcdTicks, sizeof(int) },
|
|
{ &gbLcdLYIncrementTicks, sizeof(int) },
|
|
{ &gbTimerTicks, sizeof(int) },
|
|
{ &gbTimerClockTicks, sizeof(int) },
|
|
{ &gbSerialTicks, sizeof(int) },
|
|
{ &gbSerialBits, sizeof(int) },
|
|
{ &gbInt48Signal, sizeof(int) },
|
|
{ &gbInterruptWait, sizeof(int) },
|
|
{ &gbSynchronizeTicks, sizeof(int) },
|
|
{ &gbTimerOn, sizeof(int) },
|
|
{ &gbTimerMode, sizeof(int) },
|
|
{ &gbSerialOn, sizeof(int) },
|
|
{ &gbWindowLine, sizeof(int) },
|
|
{ &gbCgbMode, sizeof(int) },
|
|
{ &gbVramBank, sizeof(int) },
|
|
{ &gbWramBank, sizeof(int) },
|
|
{ &gbHdmaSource, sizeof(int) },
|
|
{ &gbHdmaDestination, sizeof(int) },
|
|
{ &gbHdmaBytes, sizeof(int) },
|
|
{ &gbHdmaOn, sizeof(int) },
|
|
{ &gbSpeed, sizeof(int) },
|
|
{ &gbSgbMode, sizeof(int) },
|
|
{ ®ister_DIV, sizeof(u8) },
|
|
{ ®ister_TIMA, sizeof(u8) },
|
|
{ ®ister_TMA, sizeof(u8) },
|
|
{ ®ister_TAC, sizeof(u8) },
|
|
{ ®ister_IF, sizeof(u8) },
|
|
{ ®ister_LCDC, sizeof(u8) },
|
|
{ ®ister_STAT, sizeof(u8) },
|
|
{ ®ister_SCY, sizeof(u8) },
|
|
{ ®ister_SCX, sizeof(u8) },
|
|
{ ®ister_LY, sizeof(u8) },
|
|
{ ®ister_LYC, sizeof(u8) },
|
|
{ ®ister_DMA, sizeof(u8) },
|
|
{ ®ister_WY, sizeof(u8) },
|
|
{ ®ister_WX, sizeof(u8) },
|
|
{ ®ister_VBK, sizeof(u8) },
|
|
{ ®ister_HDMA1, sizeof(u8) },
|
|
{ ®ister_HDMA2, sizeof(u8) },
|
|
{ ®ister_HDMA3, sizeof(u8) },
|
|
{ ®ister_HDMA4, sizeof(u8) },
|
|
{ ®ister_HDMA5, sizeof(u8) },
|
|
{ ®ister_SVBK, sizeof(u8) },
|
|
{ ®ister_IE , sizeof(u8) },
|
|
{ &gbBgp[0], sizeof(u8) },
|
|
{ &gbBgp[1], sizeof(u8) },
|
|
{ &gbBgp[2], sizeof(u8) },
|
|
{ &gbBgp[3], sizeof(u8) },
|
|
{ &gbObp0[0], sizeof(u8) },
|
|
{ &gbObp0[1], sizeof(u8) },
|
|
{ &gbObp0[2], sizeof(u8) },
|
|
{ &gbObp0[3], sizeof(u8) },
|
|
{ &gbObp1[0], sizeof(u8) },
|
|
{ &gbObp1[1], sizeof(u8) },
|
|
{ &gbObp1[2], sizeof(u8) },
|
|
{ &gbObp1[3], sizeof(u8) },
|
|
{ NULL, 0 }
|
|
};
|
|
|
|
|
|
static bool gbWriteSaveState(gzFile gzFile)
|
|
{
|
|
|
|
utilWriteInt(gzFile, GBSAVE_GAME_VERSION);
|
|
|
|
utilGzWrite(gzFile, &gbRom[0x134], 15);
|
|
|
|
utilWriteInt(gzFile, useBios);
|
|
utilWriteInt(gzFile, inBios);
|
|
|
|
utilWriteData(gzFile, gbSaveGameStruct);
|
|
|
|
utilGzWrite(gzFile, &IFF, 2);
|
|
|
|
if(gbSgbMode) {
|
|
gbSgbSaveGame(gzFile);
|
|
}
|
|
|
|
utilGzWrite(gzFile, &gbDataMBC1, sizeof(gbDataMBC1));
|
|
utilGzWrite(gzFile, &gbDataMBC2, sizeof(gbDataMBC2));
|
|
utilGzWrite(gzFile, &gbDataMBC3, sizeof(gbDataMBC3));
|
|
utilGzWrite(gzFile, &gbDataMBC5, sizeof(gbDataMBC5));
|
|
utilGzWrite(gzFile, &gbDataMBC7, sizeof(gbDataMBC7));
|
|
utilGzWrite(gzFile, &gbDataHuC1, sizeof(gbDataHuC1));
|
|
utilGzWrite(gzFile, &gbDataHuC3, sizeof(gbDataHuC3));
|
|
utilGzWrite(gzFile, &gbDataTAMA5, sizeof(gbDataTAMA5));
|
|
if (gbTAMA5ram != NULL)
|
|
utilGzWrite(gzFile, gbTAMA5ram, gbTAMA5ramSize);
|
|
utilGzWrite(gzFile, &gbDataMMM01, sizeof(gbDataMMM01));
|
|
|
|
utilGzWrite(gzFile, gbPalette, 128 * sizeof(u16));
|
|
|
|
utilGzWrite(gzFile, &gbMemory[0x8000], 0x8000);
|
|
|
|
if(gbRamSize && gbRam) {
|
|
utilWriteInt(gzFile, gbRamSize);
|
|
utilGzWrite(gzFile, gbRam, gbRamSize);
|
|
}
|
|
|
|
if(gbCgbMode) {
|
|
utilGzWrite(gzFile, gbVram, 0x4000);
|
|
utilGzWrite(gzFile, gbWram, 0x8000);
|
|
}
|
|
|
|
gbSoundSaveGame(gzFile);
|
|
|
|
gbCheatsSaveGame(gzFile);
|
|
|
|
utilWriteInt(gzFile, gbLcdModeDelayed);
|
|
utilWriteInt(gzFile, gbLcdTicksDelayed);
|
|
utilWriteInt(gzFile, gbLcdLYIncrementTicksDelayed);
|
|
utilWriteInt(gzFile, gbSpritesTicks[299]);
|
|
utilWriteInt(gzFile, gbTimerModeChange);
|
|
utilWriteInt(gzFile, gbTimerOnChange);
|
|
utilWriteInt(gzFile, gbHardware);
|
|
utilWriteInt(gzFile, gbBlackScreen);
|
|
utilWriteInt(gzFile, oldRegister_WY);
|
|
utilWriteInt(gzFile, gbWindowLine);
|
|
utilWriteInt(gzFile, inUseRegister_WY);
|
|
utilWriteInt(gzFile, gbScreenOn);
|
|
utilWriteInt(gzFile, 0x12345678); // end marker
|
|
return true;
|
|
}
|
|
|
|
bool gbWriteMemSaveState(char *memory, int available)
|
|
{
|
|
gzFile gzFile = utilMemGzOpen(memory, available, "w");
|
|
|
|
if(gzFile == NULL) {
|
|
return false;
|
|
}
|
|
|
|
bool res = gbWriteSaveState(gzFile);
|
|
|
|
long pos = utilGzMemTell(gzFile)+8;
|
|
|
|
if(pos >= (available))
|
|
res = false;
|
|
|
|
utilGzClose(gzFile);
|
|
|
|
return res;
|
|
}
|
|
|
|
bool gbWriteSaveState(const char *name)
|
|
{
|
|
gzFile gzFile = utilGzOpen(name,"wb");
|
|
|
|
if(gzFile == NULL)
|
|
return false;
|
|
|
|
bool res = gbWriteSaveState(gzFile);
|
|
|
|
utilGzClose(gzFile);
|
|
return res;
|
|
}
|
|
|
|
static bool gbReadSaveState(gzFile gzFile)
|
|
{
|
|
int version = utilReadInt(gzFile);
|
|
|
|
if(version > GBSAVE_GAME_VERSION || version < 0) {
|
|
systemMessage(MSG_UNSUPPORTED_VB_SGM,
|
|
N_("Unsupported VisualBoy save game version %d"), version);
|
|
return false;
|
|
}
|
|
|
|
u8 romname[20];
|
|
|
|
utilGzRead(gzFile, romname, 15);
|
|
|
|
if(memcmp(&gbRom[0x134], romname, 15) != 0) {
|
|
systemMessage(MSG_CANNOT_LOAD_SGM_FOR,
|
|
N_("Cannot load save game for %s. Playing %s"),
|
|
romname, &gbRom[0x134]);
|
|
return false;
|
|
}
|
|
|
|
|
|
bool ub = false;
|
|
bool ib = false;
|
|
|
|
if (version >= 11)
|
|
{
|
|
ub = utilReadInt(gzFile) ? true : false;
|
|
ib = utilReadInt(gzFile) ? true : false;
|
|
|
|
if((ub != useBios) && (ib)) {
|
|
if(useBios)
|
|
systemMessage(MSG_SAVE_GAME_NOT_USING_BIOS,
|
|
N_("Save game is not using the BIOS files"));
|
|
else
|
|
systemMessage(MSG_SAVE_GAME_USING_BIOS,
|
|
N_("Save game is using the BIOS file"));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
gbReset();
|
|
|
|
inBios = ib;
|
|
|
|
utilReadData(gzFile, gbSaveGameStruct);
|
|
|
|
|
|
// Correct crash when loading color gameboy save in regular gameboy type.
|
|
if (!gbCgbMode)
|
|
{
|
|
if(gbVram != NULL) {
|
|
free(gbVram);
|
|
gbVram = NULL;
|
|
}
|
|
if(gbWram != NULL) {
|
|
free(gbWram);
|
|
gbWram = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(gbVram == NULL)
|
|
gbVram = (u8 *)malloc(0x4000);
|
|
if(gbWram == NULL)
|
|
gbWram = (u8 *)malloc(0x8000);
|
|
memset(gbVram,0,0x4000);
|
|
memset(gbPalette,0, 2*128);
|
|
}
|
|
|
|
|
|
|
|
if(version >= GBSAVE_GAME_VERSION_7) {
|
|
utilGzRead(gzFile, &IFF, 2);
|
|
}
|
|
|
|
if(gbSgbMode) {
|
|
gbSgbReadGame(gzFile, version);
|
|
} else {
|
|
gbSgbMask = 0; // loading a game at the wrong time causes no display
|
|
}
|
|
if (version<11)
|
|
utilGzRead(gzFile, &gbDataMBC1, sizeof(gbDataMBC1) - sizeof(int));
|
|
else
|
|
utilGzRead(gzFile, &gbDataMBC1, sizeof(gbDataMBC1));
|
|
utilGzRead(gzFile, &gbDataMBC2, sizeof(gbDataMBC2));
|
|
if(version < GBSAVE_GAME_VERSION_4)
|
|
// prior to version 4, there was no adjustment for the time the game
|
|
// was last played, so we have less to read. This needs update if the
|
|
// structure changes again.
|
|
utilGzRead(gzFile, &gbDataMBC3, sizeof(gbDataMBC3)-sizeof(time_t));
|
|
else
|
|
utilGzRead(gzFile, &gbDataMBC3, sizeof(gbDataMBC3));
|
|
utilGzRead(gzFile, &gbDataMBC5, sizeof(gbDataMBC5));
|
|
utilGzRead(gzFile, &gbDataMBC7, sizeof(gbDataMBC7));
|
|
utilGzRead(gzFile, &gbDataHuC1, sizeof(gbDataHuC1));
|
|
utilGzRead(gzFile, &gbDataHuC3, sizeof(gbDataHuC3));
|
|
if(version>=11)
|
|
{
|
|
utilGzRead(gzFile, &gbDataTAMA5, sizeof(gbDataTAMA5));
|
|
if(gbTAMA5ram != NULL) {
|
|
if(skipSaveGameBattery) {
|
|
utilGzSeek(gzFile, gbTAMA5ramSize, SEEK_CUR);
|
|
} else {
|
|
utilGzRead(gzFile, gbTAMA5ram, gbTAMA5ramSize);
|
|
}
|
|
}
|
|
utilGzRead(gzFile, &gbDataMMM01, sizeof(gbDataMMM01));
|
|
}
|
|
|
|
if(version < GBSAVE_GAME_VERSION_5) {
|
|
utilGzRead(gzFile, pix, 256*224*sizeof(u16));
|
|
}
|
|
memset(pix, 0, 257*226*sizeof(u32));
|
|
|
|
if(version < GBSAVE_GAME_VERSION_6) {
|
|
utilGzRead(gzFile, gbPalette, 64 * sizeof(u16));
|
|
} else
|
|
utilGzRead(gzFile, gbPalette, 128 * sizeof(u16));
|
|
|
|
if (version < 11)
|
|
utilGzRead(gzFile, gbPalette, 128 * sizeof(u16));
|
|
|
|
if(version < GBSAVE_GAME_VERSION_10) {
|
|
if(!gbCgbMode && !gbSgbMode) {
|
|
for(int i = 0; i < 8; i++)
|
|
gbPalette[i] = systemGbPalette[gbPaletteOption*8+i];
|
|
}
|
|
}
|
|
|
|
utilGzRead(gzFile, &gbMemory[0x8000], 0x8000);
|
|
|
|
if(gbRamSize && gbRam) {
|
|
if(version < 11)
|
|
if(skipSaveGameBattery) {
|
|
utilGzSeek(gzFile, gbRamSize, SEEK_CUR); //skip
|
|
} else {
|
|
utilGzRead(gzFile, gbRam, gbRamSize); //read
|
|
}
|
|
else
|
|
{
|
|
int ramSize = utilReadInt(gzFile);
|
|
if(skipSaveGameBattery) {
|
|
utilGzSeek(gzFile, (gbRamSize>ramSize) ? ramSize : gbRamSize, SEEK_CUR); //skip
|
|
} else {
|
|
utilGzRead(gzFile, gbRam, (gbRamSize>ramSize) ? ramSize : gbRamSize); //read
|
|
}
|
|
if(ramSize>gbRamSize)
|
|
utilGzSeek(gzFile,ramSize-gbRamSize,SEEK_CUR);
|
|
}
|
|
}
|
|
|
|
memset(gbSCYLine, register_SCY, sizeof(gbSCYLine));
|
|
memset(gbSCXLine, register_SCX, sizeof(gbSCXLine));
|
|
memset(gbBgpLine, (gbBgp[0] | (gbBgp[1]<<2) | (gbBgp[2]<<4) |
|
|
(gbBgp[3]<<6)), sizeof(gbBgpLine));
|
|
memset(gbObp0Line, (gbObp0[0] | (gbObp0[1]<<2) | (gbObp0[2]<<4) |
|
|
(gbObp0[3]<<6)), sizeof(gbObp0Line));
|
|
memset(gbObp1Line, (gbObp1[0] | (gbObp1[1]<<2) | (gbObp1[2]<<4) |
|
|
(gbObp1[3]<<6)), sizeof(gbObp1Line));
|
|
memset(gbSpritesTicks, 0x0, sizeof(gbSpritesTicks));
|
|
|
|
if (inBios)
|
|
{
|
|
gbMemoryMap[0x00] = &gbMemory[0x0000];
|
|
memcpy ((u8 *)(gbMemory), (u8 *)(gbRom), 0x1000);
|
|
memcpy ((u8 *)(gbMemory), (u8 *)(bios), 0x100);
|
|
}
|
|
else
|
|
gbMemoryMap[0x00] = &gbRom[0x0000];
|
|
gbMemoryMap[0x01] = &gbRom[0x1000];
|
|
gbMemoryMap[0x02] = &gbRom[0x2000];
|
|
gbMemoryMap[0x03] = &gbRom[0x3000];
|
|
gbMemoryMap[0x04] = &gbRom[0x4000];
|
|
gbMemoryMap[0x05] = &gbRom[0x5000];
|
|
gbMemoryMap[0x06] = &gbRom[0x6000];
|
|
gbMemoryMap[0x07] = &gbRom[0x7000];
|
|
gbMemoryMap[0x08] = &gbMemory[0x8000];
|
|
gbMemoryMap[0x09] = &gbMemory[0x9000];
|
|
gbMemoryMap[0x0a] = &gbMemory[0xa000];
|
|
gbMemoryMap[0x0b] = &gbMemory[0xb000];
|
|
gbMemoryMap[0x0c] = &gbMemory[0xc000];
|
|
gbMemoryMap[0x0d] = &gbMemory[0xd000];
|
|
gbMemoryMap[0x0e] = &gbMemory[0xe000];
|
|
gbMemoryMap[0x0f] = &gbMemory[0xf000];
|
|
|
|
switch(gbRomType) {
|
|
case 0x00:
|
|
case 0x01:
|
|
case 0x02:
|
|
case 0x03:
|
|
// MBC 1
|
|
memoryUpdateMapMBC1();
|
|
break;
|
|
case 0x05:
|
|
case 0x06:
|
|
// MBC2
|
|
memoryUpdateMapMBC2();
|
|
break;
|
|
case 0x0b:
|
|
case 0x0c:
|
|
case 0x0d:
|
|
// MMM01
|
|
memoryUpdateMapMMM01();
|
|
break;
|
|
case 0x0f:
|
|
case 0x10:
|
|
case 0x11:
|
|
case 0x12:
|
|
case 0x13:
|
|
// MBC 3
|
|
memoryUpdateMapMBC3();
|
|
break;
|
|
case 0x19:
|
|
case 0x1a:
|
|
case 0x1b:
|
|
// MBC5
|
|
memoryUpdateMapMBC5();
|
|
break;
|
|
case 0x1c:
|
|
case 0x1d:
|
|
case 0x1e:
|
|
// MBC 5 Rumble
|
|
memoryUpdateMapMBC5();
|
|
break;
|
|
case 0x22:
|
|
// MBC 7
|
|
memoryUpdateMapMBC7();
|
|
break;
|
|
case 0x56:
|
|
// GS3
|
|
memoryUpdateMapGS3();
|
|
break;
|
|
case 0xfd:
|
|
// TAMA5
|
|
memoryUpdateMapTAMA5();
|
|
break;
|
|
case 0xfe:
|
|
// HuC3
|
|
memoryUpdateMapHuC3();
|
|
break;
|
|
case 0xff:
|
|
// HuC1
|
|
memoryUpdateMapHuC1();
|
|
break;
|
|
}
|
|
|
|
if(gbCgbMode) {
|
|
utilGzRead(gzFile, gbVram, 0x4000);
|
|
utilGzRead(gzFile, gbWram, 0x8000);
|
|
|
|
int value = register_SVBK;
|
|
if(value == 0)
|
|
value = 1;
|
|
|
|
gbMemoryMap[0x08] = &gbVram[register_VBK * 0x2000];
|
|
gbMemoryMap[0x09] = &gbVram[register_VBK * 0x2000 + 0x1000];
|
|
gbMemoryMap[0x0d] = &gbWram[value * 0x1000];
|
|
}
|
|
|
|
gbSoundReadGame(version, gzFile);
|
|
|
|
if (gbCgbMode && gbSgbMode) {
|
|
gbSgbMode = 0;
|
|
}
|
|
|
|
if(gbBorderOn && !gbSgbMask) {
|
|
gbSgbRenderBorder();
|
|
}
|
|
|
|
systemDrawScreen();
|
|
|
|
if(version > GBSAVE_GAME_VERSION_1)
|
|
{
|
|
if( skipSaveGameCheats ) {
|
|
gbCheatsReadGameSkip(gzFile, version);
|
|
} else {
|
|
gbCheatsReadGame(gzFile, version);
|
|
}
|
|
}
|
|
|
|
if (version<11)
|
|
{
|
|
gbWriteMemory(0xff00, 0);
|
|
gbMemory[0xff04] = register_DIV;
|
|
gbMemory[0xff05] = register_TIMA;
|
|
gbMemory[0xff06] = register_TMA;
|
|
gbMemory[0xff07] = register_TAC;
|
|
gbMemory[0xff40] = register_LCDC;
|
|
gbMemory[0xff42] = register_SCY;
|
|
gbMemory[0xff43] = register_SCX;
|
|
gbMemory[0xff44] = register_LY;
|
|
gbMemory[0xff45] = register_LYC;
|
|
gbMemory[0xff46] = register_DMA;
|
|
gbMemory[0xff4a] = register_WY;
|
|
gbMemory[0xff4b] = register_WX;
|
|
gbMemory[0xff4f] = register_VBK;
|
|
gbMemory[0xff51] = register_HDMA1;
|
|
gbMemory[0xff52] = register_HDMA2;
|
|
gbMemory[0xff53] = register_HDMA3;
|
|
gbMemory[0xff54] = register_HDMA4;
|
|
gbMemory[0xff55] = register_HDMA5;
|
|
gbMemory[0xff70] = register_SVBK;
|
|
gbMemory[0xffff] = register_IE;
|
|
GBDIV_CLOCK_TICKS = 64;
|
|
|
|
if (gbSpeed)
|
|
gbDivTicks /=2;
|
|
|
|
if ((gbLcdMode == 0) && (register_STAT & 8))
|
|
gbInt48Signal |= 1;
|
|
if ((gbLcdMode == 1) && (register_STAT & 0x10))
|
|
gbInt48Signal |= 2;
|
|
if ((gbLcdMode == 2) && (register_STAT & 0x20))
|
|
gbInt48Signal |= 4;
|
|
if ((register_LY==register_LYC) && (register_STAT & 0x40))
|
|
gbInt48Signal |= 8;
|
|
|
|
gbLcdLYIncrementTicks = GBLY_INCREMENT_CLOCK_TICKS;
|
|
|
|
if (gbLcdMode == 2)
|
|
gbLcdLYIncrementTicks-=GBLCD_MODE_2_CLOCK_TICKS-gbLcdTicks;
|
|
else if (gbLcdMode == 3)
|
|
gbLcdLYIncrementTicks -=GBLCD_MODE_2_CLOCK_TICKS+GBLCD_MODE_3_CLOCK_TICKS-gbLcdTicks;
|
|
else if (gbLcdMode == 0)
|
|
gbLcdLYIncrementTicks =gbLcdTicks;
|
|
else if (gbLcdMode == 1)
|
|
{
|
|
gbLcdLYIncrementTicks = gbLcdTicks % GBLY_INCREMENT_CLOCK_TICKS;
|
|
if (register_LY == 0x99)
|
|
gbLcdLYIncrementTicks =gbLine99Ticks;
|
|
else if (register_LY == 0)
|
|
gbLcdLYIncrementTicks += GBLY_INCREMENT_CLOCK_TICKS;
|
|
}
|
|
|
|
gbLcdModeDelayed = gbLcdMode;
|
|
gbLcdTicksDelayed = gbLcdTicks--;
|
|
gbLcdLYIncrementTicksDelayed = gbLcdLYIncrementTicks--;
|
|
gbInterruptWait = 0;
|
|
memset(gbSpritesTicks,0,sizeof(gbSpritesTicks));
|
|
}
|
|
else
|
|
{
|
|
gbLcdModeDelayed = utilReadInt(gzFile);
|
|
gbLcdTicksDelayed = utilReadInt(gzFile);
|
|
gbLcdLYIncrementTicksDelayed = utilReadInt(gzFile);
|
|
gbSpritesTicks[299] = utilReadInt(gzFile) & 0xff;
|
|
gbTimerModeChange = (utilReadInt(gzFile) ? true : false);
|
|
gbTimerOnChange = (utilReadInt(gzFile) ? true : false);
|
|
gbHardware = utilReadInt(gzFile);
|
|
gbBlackScreen = (utilReadInt(gzFile) ? true : false);
|
|
oldRegister_WY = utilReadInt(gzFile);
|
|
gbWindowLine = utilReadInt(gzFile);
|
|
inUseRegister_WY = utilReadInt(gzFile);
|
|
gbScreenOn = (utilReadInt(gzFile) ? true : false);
|
|
}
|
|
|
|
if (gbSpeed)
|
|
gbLine99Ticks *= 2;
|
|
|
|
systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED;
|
|
|
|
if ( version >= 12 && utilReadInt( gzFile ) != 0x12345678 )
|
|
assert( false ); // fails if something read too much/little from file
|
|
|
|
return true;
|
|
}
|
|
|
|
bool gbReadMemSaveState(char *memory, int available)
|
|
{
|
|
gzFile gzFile = utilMemGzOpen(memory, available, "r");
|
|
|
|
bool res = gbReadSaveState(gzFile);
|
|
|
|
utilGzClose(gzFile);
|
|
|
|
return res;
|
|
}
|
|
|
|
bool gbReadSaveState(const char *name)
|
|
{
|
|
gzFile gzFile = utilGzOpen(name,"rb");
|
|
|
|
if(gzFile == NULL) {
|
|
return false;
|
|
}
|
|
|
|
bool res = gbReadSaveState(gzFile);
|
|
|
|
utilGzClose(gzFile);
|
|
|
|
return res;
|
|
}
|
|
|
|
bool gbWritePNGFile(const char *fileName)
|
|
{
|
|
/* if(gbBorderOn)
|
|
return utilWritePNGFile(fileName, 256, 224, pix);
|
|
return utilWritePNGFile(fileName, 160, 144, pix);*/
|
|
return false;
|
|
}
|
|
|
|
bool gbWriteBMPFile(const char *fileName)
|
|
{
|
|
/* if(gbBorderOn)
|
|
return utilWriteBMPFile(fileName, 256, 224, pix);
|
|
return utilWriteBMPFile(fileName, 160, 144, pix);*/
|
|
return false;
|
|
}
|
|
|
|
void gbCleanUp()
|
|
{
|
|
if(gbRam != NULL) {
|
|
free(gbRam);
|
|
gbRam = NULL;
|
|
}
|
|
|
|
if(gbRom != NULL) {
|
|
free(gbRom);
|
|
gbRom = NULL;
|
|
}
|
|
|
|
if(bios != NULL) {
|
|
free(bios);
|
|
bios = NULL;
|
|
}
|
|
|
|
if(gbMemory != NULL) {
|
|
free(gbMemory);
|
|
gbMemory = NULL;
|
|
}
|
|
|
|
if(gbLineBuffer != NULL) {
|
|
free(gbLineBuffer);
|
|
gbLineBuffer = NULL;
|
|
}
|
|
|
|
if(pix != NULL) {
|
|
free(pix);
|
|
pix = NULL;
|
|
}
|
|
|
|
gbSgbShutdown();
|
|
|
|
if(gbVram != NULL) {
|
|
free(gbVram);
|
|
gbVram = NULL;
|
|
}
|
|
|
|
if(gbWram != NULL) {
|
|
free(gbWram);
|
|
gbWram = NULL;
|
|
}
|
|
|
|
if(gbTAMA5ram != NULL) {
|
|
free(gbTAMA5ram);
|
|
gbTAMA5ram = NULL;
|
|
}
|
|
|
|
systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED;
|
|
}
|
|
|
|
bool gbLoadRom(const char *szFile)
|
|
{
|
|
int size = 0;
|
|
|
|
if(gbRom != NULL) {
|
|
gbCleanUp();
|
|
}
|
|
|
|
systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED;
|
|
|
|
/* gbRom = utilLoad(szFile,
|
|
utilIsGBImage,
|
|
NULL,
|
|
size);*/
|
|
if(!gbRom)
|
|
return false;
|
|
|
|
gbRomSize = size;
|
|
|
|
gbBatteryError = false;
|
|
|
|
if(bios != NULL) {
|
|
free(bios);
|
|
bios = NULL;
|
|
}
|
|
bios = (u8 *)calloc(1,0x100);
|
|
|
|
return gbUpdateSizes();
|
|
}
|
|
|
|
bool gbUpdateSizes()
|
|
{
|
|
if(gbRom[0x148] > 8) {
|
|
systemMessage(MSG_UNSUPPORTED_ROM_SIZE,
|
|
N_("Unsupported rom size %02x"), gbRom[0x148]);
|
|
return false;
|
|
}
|
|
|
|
if(gbRomSize < gbRomSizes[gbRom[0x148]]) {
|
|
u8 *gbRomNew = (u8 *)realloc(gbRom, gbRomSizes[gbRom[0x148]]);
|
|
if( !gbRomNew ) { assert( false ); return false; };
|
|
gbRom = gbRomNew;
|
|
for (int i = gbRomSize; i<gbRomSizes[gbRom[0x148]]; i++)
|
|
gbRom[i] = 0x00; // Not sure if it's 0x00, 0xff or random data...
|
|
}
|
|
// (it's in the case a cart is 'lying' on its size.
|
|
else if ((gbRomSize>gbRomSizes[gbRom[0x148]]) && (genericflashcardEnable))
|
|
{
|
|
gbRomSize = gbRomSize>>16;
|
|
gbRom[0x148] = 0;
|
|
if (gbRomSize)
|
|
{
|
|
while (!((gbRomSize & 1) || (gbRom[0x148] == 7)))
|
|
{
|
|
gbRom[0x148]++;
|
|
gbRomSize>>=1;
|
|
}
|
|
gbRom[0x148]++;
|
|
}
|
|
u8 *gbRomNew = (u8 *)realloc(gbRom, gbRomSizes[gbRom[0x148]]);
|
|
if( !gbRomNew ) { assert( false ); return false; };
|
|
gbRom = gbRomNew;
|
|
}
|
|
gbRomSize = gbRomSizes[gbRom[0x148]];
|
|
gbRomSizeMask = gbRomSizesMasks[gbRom[0x148]];
|
|
|
|
|
|
// The 'genericflashcard' option allows some PD to work.
|
|
// However, the setting is dangerous (if you let in enabled
|
|
// and play a normal game, it might just break everything).
|
|
// That's why it is not saved in the emulator options.
|
|
// Also I added some checks in VBA to make sure your saves will not be
|
|
// overwritten if you wrongly enable this option for a game
|
|
// you already played (and vice-versa, ie. if you forgot to
|
|
// enable the option for a game you played with it enabled, like Shawu Story).
|
|
u8 ramsize = genericflashcardEnable ? 5 : gbRom[0x149];
|
|
gbRom[0x149] = ramsize;
|
|
|
|
if ((gbRom[2] == 0x6D) && (gbRom[5] == 0x47) && (gbRom[6] == 0x65) && (gbRom[7] == 0x6E) &&
|
|
(gbRom[8] == 0x69) && (gbRom[9] == 0x65) && (gbRom[0xA] == 0x28) && (gbRom[0xB] == 0x54))
|
|
{
|
|
gbCheatingDevice = 1; // GameGenie
|
|
for (int i = 0; i < 0x20; i++) // Cleans GG hardware registers
|
|
gbRom[0x4000+i] = 0;
|
|
}
|
|
else if (((gbRom[0x104] == 0x44) && (gbRom[0x156] == 0xEA) && (gbRom[0x158] == 0x7F) &&
|
|
(gbRom[0x159] == 0xEA) && (gbRom[0x15B] == 0x7F)) || ((gbRom[0x165] == 0x3E) &&
|
|
(gbRom[0x166] == 0xD9) && (gbRom[0x16D] == 0xE1) && (gbRom[0x16E] == 0x7F)))
|
|
gbCheatingDevice = 2; // GameShark
|
|
else gbCheatingDevice = 0;
|
|
|
|
if(ramsize > 5) {
|
|
systemMessage(MSG_UNSUPPORTED_RAM_SIZE,
|
|
N_("Unsupported ram size %02x"), gbRom[0x149]);
|
|
return false;
|
|
}
|
|
|
|
gbRamSize = gbRamSizes[ramsize];
|
|
gbRamSizeMask = gbRamSizesMasks[ramsize];
|
|
|
|
gbRomType = gbRom[0x147];
|
|
if (genericflashcardEnable)
|
|
{
|
|
/*if (gbRomType<2)
|
|
gbRomType =3;
|
|
else if ((gbRomType == 0xc) || (gbRomType == 0xf) || (gbRomType == 0x12) ||
|
|
(gbRomType == 0x16) || (gbRomType == 0x1a) || (gbRomType == 0x1d))
|
|
gbRomType++;
|
|
else if ((gbRomType == 0xb) || (gbRomType == 0x11) || (gbRomType == 0x15) ||
|
|
(gbRomType == 0x19) || (gbRomType == 0x1c))
|
|
gbRomType+=2;
|
|
else if ((gbRomType == 0x5) || (gbRomType == 0x6))
|
|
gbRomType = 0x1a;*/
|
|
gbRomType = 0x1b;
|
|
}
|
|
else if (gbCheatingDevice == 1)
|
|
gbRomType = 0x55;
|
|
else if (gbCheatingDevice == 2)
|
|
gbRomType = 0x56;
|
|
|
|
gbRom[0x147] = gbRomType;
|
|
|
|
mapperReadRAM = NULL;
|
|
|
|
switch(gbRomType) {
|
|
case 0x00:
|
|
case 0x01:
|
|
case 0x02:
|
|
case 0x03:
|
|
case 0x08:
|
|
case 0x09:
|
|
// MBC 1
|
|
mapper = mapperMBC1ROM;
|
|
mapperRAM = mapperMBC1RAM;
|
|
mapperReadRAM = mapperMBC1ReadRAM;
|
|
break;
|
|
case 0x05:
|
|
case 0x06:
|
|
// MBC2
|
|
mapper = mapperMBC2ROM;
|
|
mapperRAM = mapperMBC2RAM;
|
|
gbRamSize = 0x200;
|
|
gbRamSizeMask = 0x1ff;
|
|
break;
|
|
case 0x0b:
|
|
case 0x0c:
|
|
case 0x0d:
|
|
// MMM01
|
|
mapper = mapperMMM01ROM;
|
|
mapperRAM = mapperMMM01RAM;
|
|
break;
|
|
case 0x0f:
|
|
case 0x10:
|
|
case 0x11:
|
|
case 0x12:
|
|
case 0x13:
|
|
case 0xfc:
|
|
// MBC 3
|
|
mapper = mapperMBC3ROM;
|
|
mapperRAM = mapperMBC3RAM;
|
|
mapperReadRAM = mapperMBC3ReadRAM;
|
|
break;
|
|
case 0x19:
|
|
case 0x1a:
|
|
case 0x1b:
|
|
// MBC5
|
|
mapper = mapperMBC5ROM;
|
|
mapperRAM = mapperMBC5RAM;
|
|
mapperReadRAM = mapperMBC5ReadRAM;
|
|
break;
|
|
case 0x1c:
|
|
case 0x1d:
|
|
case 0x1e:
|
|
// MBC 5 Rumble
|
|
mapper = mapperMBC5ROM;
|
|
mapperRAM = mapperMBC5RAM;
|
|
mapperReadRAM = mapperMBC5ReadRAM;
|
|
break;
|
|
case 0x22:
|
|
// MBC 7
|
|
mapper = mapperMBC7ROM;
|
|
mapperRAM = mapperMBC7RAM;
|
|
mapperReadRAM = mapperMBC7ReadRAM;
|
|
gbRamSize = 0x200;
|
|
gbRamSizeMask = 0x1ff;
|
|
break;
|
|
// GG (GameGenie)
|
|
case 0x55:
|
|
mapper = mapperGGROM;
|
|
break;
|
|
case 0x56:
|
|
// GS (GameShark)
|
|
mapper = mapperGS3ROM;
|
|
break;
|
|
case 0xfd:
|
|
// TAMA5
|
|
if (gbRam!= NULL)
|
|
{
|
|
free(gbRam);
|
|
gbRam = NULL;
|
|
}
|
|
|
|
ramsize = 3;
|
|
gbRamSize = gbRamSizes[3];
|
|
gbRamSizeMask = gbRamSizesMasks[3];
|
|
gbRamFill = 0x0;
|
|
|
|
gbTAMA5ramSize = 0x100;
|
|
|
|
if (gbTAMA5ram == NULL)
|
|
gbTAMA5ram = (u8 *)malloc(gbTAMA5ramSize);
|
|
memset(gbTAMA5ram, 0x0, gbTAMA5ramSize);
|
|
|
|
mapperRAM = mapperTAMA5RAM;
|
|
mapperReadRAM = mapperTAMA5ReadRAM;
|
|
mapperUpdateClock = memoryUpdateTAMA5Clock;
|
|
break;
|
|
case 0xfe:
|
|
// HuC3
|
|
mapper = mapperHuC3ROM;
|
|
mapperRAM = mapperHuC3RAM;
|
|
mapperReadRAM = mapperHuC3ReadRAM;
|
|
break;
|
|
case 0xff:
|
|
// HuC1
|
|
mapper = mapperHuC1ROM;
|
|
mapperRAM = mapperHuC1RAM;
|
|
break;
|
|
default:
|
|
systemMessage(MSG_UNKNOWN_CARTRIDGE_TYPE,
|
|
N_("Unknown cartridge type %02x"), gbRomType);
|
|
return false;
|
|
}
|
|
|
|
if(gbRamSize) {
|
|
// Always allocate 4 KiB to prevent access issues down the line.
|
|
gbRam = (u8 *)malloc(std::max(4096, gbRamSize));
|
|
memset(gbRam, gbRamFill, gbRamSize);
|
|
}
|
|
|
|
switch(gbRomType) {
|
|
case 0x03:
|
|
case 0x06:
|
|
case 0x0f:
|
|
case 0x10:
|
|
case 0x13:
|
|
case 0x1b:
|
|
case 0x1d:
|
|
case 0x1e:
|
|
case 0x22:
|
|
case 0xfd:
|
|
case 0xff:
|
|
gbBattery = 1;
|
|
break;
|
|
}
|
|
|
|
gbInit();
|
|
|
|
//gbReset();
|
|
|
|
switch(gbRomType) {
|
|
case 0x1c:
|
|
case 0x1d:
|
|
case 0x1e:
|
|
gbDataMBC5.isRumbleCartridge = 1;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
int gbGetNextEvent (int _clockTicks)
|
|
{
|
|
if (register_LCDC & 0x80)
|
|
{
|
|
if(gbLcdTicks < _clockTicks)
|
|
_clockTicks = gbLcdTicks;
|
|
|
|
if(gbLcdTicksDelayed < _clockTicks)
|
|
_clockTicks = gbLcdTicksDelayed;
|
|
|
|
if(gbLcdLYIncrementTicksDelayed < _clockTicks)
|
|
_clockTicks = gbLcdLYIncrementTicksDelayed;
|
|
}
|
|
|
|
if(gbLcdLYIncrementTicks < _clockTicks)
|
|
_clockTicks = gbLcdLYIncrementTicks;
|
|
|
|
if(gbSerialOn && (gbSerialTicks < _clockTicks))
|
|
_clockTicks = gbSerialTicks;
|
|
|
|
if(gbTimerOn && (((gbInternalTimer) & gbTimerMask[gbTimerMode])+1 < _clockTicks))
|
|
_clockTicks = ((gbInternalTimer) & gbTimerMask[gbTimerMode])+1;
|
|
|
|
//if(soundTicks && (soundTicks < _clockTicks))
|
|
// _clockTicks = soundTicks;
|
|
|
|
if ((_clockTicks<=0) || (gbInterruptWait))
|
|
_clockTicks = 1;
|
|
|
|
return _clockTicks;
|
|
}
|
|
|
|
void gbDrawLine()
|
|
{
|
|
switch(systemColorDepth) {
|
|
case 16:
|
|
{
|
|
u16 * dest = (u16 *)pix +
|
|
(gbBorderLineSkip+2) * (register_LY + gbBorderRowSkip)
|
|
+ gbBorderColumnSkip;
|
|
for(int x = 0; x < 160; ) {
|
|
*dest++ = systemColorMap16[gbLineMix[x++]];
|
|
*dest++ = systemColorMap16[gbLineMix[x++]];
|
|
*dest++ = systemColorMap16[gbLineMix[x++]];
|
|
*dest++ = systemColorMap16[gbLineMix[x++]];
|
|
|
|
*dest++ = systemColorMap16[gbLineMix[x++]];
|
|
*dest++ = systemColorMap16[gbLineMix[x++]];
|
|
*dest++ = systemColorMap16[gbLineMix[x++]];
|
|
*dest++ = systemColorMap16[gbLineMix[x++]];
|
|
|
|
*dest++ = systemColorMap16[gbLineMix[x++]];
|
|
*dest++ = systemColorMap16[gbLineMix[x++]];
|
|
*dest++ = systemColorMap16[gbLineMix[x++]];
|
|
*dest++ = systemColorMap16[gbLineMix[x++]];
|
|
|
|
*dest++ = systemColorMap16[gbLineMix[x++]];
|
|
*dest++ = systemColorMap16[gbLineMix[x++]];
|
|
*dest++ = systemColorMap16[gbLineMix[x++]];
|
|
*dest++ = systemColorMap16[gbLineMix[x++]];
|
|
}
|
|
if(gbBorderOn)
|
|
dest += gbBorderColumnSkip;
|
|
*dest++ = 0; // for filters that read one pixel more
|
|
}
|
|
break;
|
|
|
|
case 24:
|
|
{
|
|
u8 *dest = (u8 *)pix +
|
|
3*(gbBorderLineSkip * (register_LY + gbBorderRowSkip) +
|
|
gbBorderColumnSkip);
|
|
for(int x = 0; x < 160;) {
|
|
*((u32 *)dest) = systemColorMap32[gbLineMix[x++]];
|
|
dest+= 3;
|
|
*((u32 *)dest) = systemColorMap32[gbLineMix[x++]];
|
|
dest+= 3;
|
|
*((u32 *)dest) = systemColorMap32[gbLineMix[x++]];
|
|
dest+= 3;
|
|
*((u32 *)dest) = systemColorMap32[gbLineMix[x++]];
|
|
dest+= 3;
|
|
|
|
*((u32 *)dest) = systemColorMap32[gbLineMix[x++]];
|
|
dest+= 3;
|
|
*((u32 *)dest) = systemColorMap32[gbLineMix[x++]];
|
|
dest+= 3;
|
|
*((u32 *)dest) = systemColorMap32[gbLineMix[x++]];
|
|
dest+= 3;
|
|
*((u32 *)dest) = systemColorMap32[gbLineMix[x++]];
|
|
dest+= 3;
|
|
|
|
*((u32 *)dest) = systemColorMap32[gbLineMix[x++]];
|
|
dest+= 3;
|
|
*((u32 *)dest) = systemColorMap32[gbLineMix[x++]];
|
|
dest+= 3;
|
|
*((u32 *)dest) = systemColorMap32[gbLineMix[x++]];
|
|
dest+= 3;
|
|
*((u32 *)dest) = systemColorMap32[gbLineMix[x++]];
|
|
dest+= 3;
|
|
|
|
*((u32 *)dest) = systemColorMap32[gbLineMix[x++]];
|
|
dest+= 3;
|
|
*((u32 *)dest) = systemColorMap32[gbLineMix[x++]];
|
|
dest+= 3;
|
|
*((u32 *)dest) = systemColorMap32[gbLineMix[x++]];
|
|
dest+= 3;
|
|
*((u32 *)dest) = systemColorMap32[gbLineMix[x++]];
|
|
dest+= 3;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 32:
|
|
{
|
|
u32 * dest = (u32 *)pix +
|
|
(gbBorderLineSkip+1) * (register_LY + gbBorderRowSkip)
|
|
+ gbBorderColumnSkip;
|
|
for(int x = 0; x < 160;) {
|
|
*dest++ = systemColorMap32[gbLineMix[x++]];
|
|
*dest++ = systemColorMap32[gbLineMix[x++]];
|
|
*dest++ = systemColorMap32[gbLineMix[x++]];
|
|
*dest++ = systemColorMap32[gbLineMix[x++]];
|
|
|
|
*dest++ = systemColorMap32[gbLineMix[x++]];
|
|
*dest++ = systemColorMap32[gbLineMix[x++]];
|
|
*dest++ = systemColorMap32[gbLineMix[x++]];
|
|
*dest++ = systemColorMap32[gbLineMix[x++]];
|
|
|
|
*dest++ = systemColorMap32[gbLineMix[x++]];
|
|
*dest++ = systemColorMap32[gbLineMix[x++]];
|
|
*dest++ = systemColorMap32[gbLineMix[x++]];
|
|
*dest++ = systemColorMap32[gbLineMix[x++]];
|
|
|
|
*dest++ = systemColorMap32[gbLineMix[x++]];
|
|
*dest++ = systemColorMap32[gbLineMix[x++]];
|
|
*dest++ = systemColorMap32[gbLineMix[x++]];
|
|
*dest++ = systemColorMap32[gbLineMix[x++]];
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void gbEmulate(int ticksToStop)
|
|
{
|
|
gbRegister tempRegister;
|
|
u8 tempValue;
|
|
s8 offset;
|
|
|
|
clockTicks = 0;
|
|
gbDmaTicks = 0;
|
|
|
|
int opcode = 0;
|
|
|
|
int opcode1 = 0;
|
|
int opcode2 = 0;
|
|
bool execute = false;
|
|
|
|
while(1) {
|
|
#ifndef FINAL_VERSION
|
|
if(systemDebug) {
|
|
if(!(IFF & 0x80)) {
|
|
if(systemDebug > 1) {
|
|
sprintf(gbBuffer,"PC=%04x AF=%04x BC=%04x DE=%04x HL=%04x SP=%04x I=%04x\n",
|
|
PC.W, AF.W, BC.W, DE.W,HL.W,SP.W,IFF);
|
|
} else {
|
|
sprintf(gbBuffer,"PC=%04x I=%02x\n", PC.W, IFF);
|
|
}
|
|
log(gbBuffer);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
u16 oldPCW = PC.W;
|
|
|
|
if(IFF & 0x80) {
|
|
if(register_LCDC & 0x80) {
|
|
clockTicks = gbLcdTicks;
|
|
} else
|
|
clockTicks = 1000;
|
|
|
|
clockTicks = gbGetNextEvent(clockTicks);
|
|
|
|
/*if(gbLcdTicksDelayed < clockTicks)
|
|
clockTicks = gbLcdTicksDelayed;
|
|
|
|
if(gbLcdLYIncrementTicksDelayed < clockTicks)
|
|
clockTicks = gbLcdLYIncrementTicksDelayed;
|
|
|
|
if(gbLcdLYIncrementTicks < clockTicks)
|
|
clockTicks = gbLcdLYIncrementTicks;
|
|
|
|
if(gbSerialOn && (gbSerialTicks < clockTicks))
|
|
clockTicks = gbSerialTicks;
|
|
|
|
if(gbTimerOn && (((gbInternalTimer) & gbTimerMask[gbTimerMode])+1 < clockTicks))
|
|
clockTicks = ((gbInternalTimer) & gbTimerMask[gbTimerMode])+1;
|
|
|
|
if(soundTicks && (soundTicks < clockTicks))
|
|
clockTicks = soundTicks;
|
|
|
|
if ((clockTicks<=0) || (gbInterruptWait))
|
|
clockTicks = 1;*/
|
|
|
|
} else {
|
|
|
|
// First we apply the clockTicks, then we execute the opcodes.
|
|
opcode1 = 0;
|
|
opcode2 = 0;
|
|
execute = true;
|
|
|
|
opcode2 = opcode1 = opcode = gbReadOpcode(PC.W++);
|
|
|
|
// If HALT state was launched while IME = 0 and (register_IF & register_IE & 0x1F),
|
|
// PC.W is not incremented for the first byte of the next instruction.
|
|
if (IFF & 2)
|
|
{
|
|
PC.W--;
|
|
IFF &= ~2;
|
|
}
|
|
|
|
clockTicks = gbCycles[opcode];
|
|
|
|
switch(opcode) {
|
|
case 0xCB:
|
|
// extended opcode
|
|
opcode2 = opcode = gbReadOpcode(PC.W++);
|
|
clockTicks = gbCyclesCB[opcode];
|
|
break;
|
|
}
|
|
gbOldClockTicks = clockTicks-1;
|
|
gbIntBreak = 1;
|
|
}
|
|
|
|
|
|
if(!emulating)
|
|
return;
|
|
|
|
// For 'breakpoint' support (opcode 0xFC is considered as a breakpoint)
|
|
if ((clockTicks==0) && execute)
|
|
{
|
|
PC.W = oldPCW;
|
|
return;
|
|
}
|
|
|
|
|
|
if (!(IFF & 0x80))
|
|
clockTicks = 1;
|
|
|
|
gbRedoLoop:
|
|
|
|
|
|
|
|
if (gbInterruptWait)
|
|
gbInterruptWait = 0;
|
|
else
|
|
gbInterruptLaunched = 0;
|
|
|
|
|
|
// Used for the EI/DI instruction's delay.
|
|
if (IFF & 0x38)
|
|
{
|
|
int tempIFF = (IFF >> 4) & 3;
|
|
|
|
if (tempIFF <=clockTicks)
|
|
{
|
|
tempIFF = 0;
|
|
IFF |=1;
|
|
}
|
|
else
|
|
tempIFF -= clockTicks;
|
|
IFF = (IFF & 0xCF) | (tempIFF <<4);
|
|
|
|
if (IFF & 0x08)
|
|
IFF &= 0x82;
|
|
}
|
|
|
|
|
|
if (register_LCDCBusy)
|
|
{
|
|
register_LCDCBusy-=clockTicks;
|
|
if (register_LCDCBusy<0)
|
|
register_LCDCBusy = 0;
|
|
}
|
|
|
|
|
|
if(gbSgbMode) {
|
|
if(gbSgbPacketTimeout) {
|
|
gbSgbPacketTimeout -= clockTicks;
|
|
|
|
if(gbSgbPacketTimeout <= 0)
|
|
gbSgbResetPacketState();
|
|
}
|
|
}
|
|
|
|
ticksToStop -= clockTicks;
|
|
|
|
// DIV register emulation
|
|
gbDivTicks -= clockTicks;
|
|
while(gbDivTicks <= 0) {
|
|
gbMemory[0xff04] = ++register_DIV;
|
|
gbDivTicks += GBDIV_CLOCK_TICKS;
|
|
}
|
|
|
|
if(register_LCDC & 0x80) {
|
|
// LCD stuff
|
|
|
|
gbLcdTicks -= clockTicks;
|
|
gbLcdTicksDelayed -= clockTicks;
|
|
gbLcdLYIncrementTicks -= clockTicks;
|
|
gbLcdLYIncrementTicksDelayed -= clockTicks;
|
|
|
|
|
|
// our counters are off, see what we need to do
|
|
|
|
// This looks (and kinda is) complicated, however this
|
|
// is the only way I found to emulate properly the way
|
|
// the real hardware operates...
|
|
while(((gbLcdTicks <= 0) && (gbLCDChangeHappened == false)) ||
|
|
((gbLcdTicksDelayed <= 0) && (gbLCDChangeHappened == true)) ||
|
|
((gbLcdLYIncrementTicks <= 0) && (gbLYChangeHappened == false)) ||
|
|
((gbLcdLYIncrementTicksDelayed<=0) && (gbLYChangeHappened == true)))
|
|
{
|
|
|
|
if ((gbLcdLYIncrementTicks <= 0) && (!gbLYChangeHappened))
|
|
{
|
|
gbLYChangeHappened = true;
|
|
gbMemory[0xff44] = register_LY = (register_LY + 1) % 154;
|
|
|
|
if (register_LY == 0x91)
|
|
{
|
|
/* if (IFF & 0x80)
|
|
gbScreenOn = !gbScreenOn;
|
|
else*/ if (register_LCDC & 0x80)
|
|
gbScreenOn = true;
|
|
}
|
|
|
|
gbLcdLYIncrementTicks += GBLY_INCREMENT_CLOCK_TICKS;
|
|
|
|
if (gbLcdMode == 1)
|
|
{
|
|
|
|
if(register_LY == 153)
|
|
gbLcdLYIncrementTicks -= GBLY_INCREMENT_CLOCK_TICKS - gbLine99Ticks;
|
|
else if(register_LY == 0)
|
|
gbLcdLYIncrementTicks += GBLY_INCREMENT_CLOCK_TICKS - gbLine99Ticks;
|
|
}
|
|
|
|
// GB only 'bug' : Halt state is broken one tick before LY==LYC interrupt
|
|
// is reflected in the registers.
|
|
if ((gbHardware & 5) && (IFF & 0x80) && (register_LY == register_LYC)
|
|
&& (register_STAT & 0x40) && (register_LY != 0))
|
|
{
|
|
if (!((gbLcdModeDelayed != 1) && (register_LY==0)))
|
|
{
|
|
gbInt48Signal &= ~9;
|
|
gbCompareLYToLYC();
|
|
gbLYChangeHappened = false;
|
|
gbMemory[0xff41] = register_STAT;
|
|
gbMemory[0xff0f] = register_IF;
|
|
}
|
|
|
|
gbLcdLYIncrementTicksDelayed += GBLY_INCREMENT_CLOCK_TICKS - gbLine99Ticks+1;
|
|
}
|
|
}
|
|
|
|
|
|
if ((gbLcdTicks <= 0) && (!gbLCDChangeHappened))
|
|
{
|
|
gbLCDChangeHappened = true;
|
|
|
|
switch(gbLcdMode)
|
|
{
|
|
case 0:
|
|
{
|
|
// H-Blank
|
|
// check if we reached the V-Blank period
|
|
if(register_LY == 144) {
|
|
// Yes, V-Blank
|
|
// set the LY increment counter
|
|
if (gbHardware & 0x5)
|
|
{
|
|
register_IF |= 1; // V-Blank interrupt
|
|
}
|
|
|
|
gbInt48Signal &= ~6;
|
|
if(register_STAT & 0x10)
|
|
{
|
|
// send LCD interrupt only if no interrupt 48h signal...
|
|
if ((!(gbInt48Signal & 1)) && ((!(gbInt48Signal & 8)) || (gbHardware & 0x0a)))
|
|
{
|
|
register_IF |=2;
|
|
gbInterruptLaunched |= 2;
|
|
if (gbHardware & 0xa)
|
|
gbInterruptWait = 1;
|
|
}
|
|
gbInt48Signal |= 2;
|
|
}
|
|
gbInt48Signal &= ~1;
|
|
|
|
gbLcdTicks += GBLCD_MODE_1_CLOCK_TICKS;
|
|
gbLcdMode = 1;
|
|
|
|
} else {
|
|
// go the the OAM being accessed mode
|
|
gbLcdTicks += GBLCD_MODE_2_CLOCK_TICKS;
|
|
gbLcdMode = 2;
|
|
|
|
gbInt48Signal &= ~6;
|
|
if(register_STAT & 0x20)
|
|
{
|
|
// send LCD interrupt only if no interrupt 48h signal...
|
|
if (!gbInt48Signal)
|
|
{
|
|
register_IF |= 2;
|
|
gbInterruptLaunched |= 2;
|
|
}
|
|
gbInt48Signal |= 4;
|
|
}
|
|
gbInt48Signal &= ~1;
|
|
}
|
|
}
|
|
break;
|
|
case 1:
|
|
{
|
|
// V-Blank
|
|
// next mode is OAM being accessed mode
|
|
gbInt48Signal &= ~5;
|
|
if(register_STAT & 0x20)
|
|
{
|
|
// send LCD interrupt only if no interrupt 48h signal...
|
|
if (!gbInt48Signal)
|
|
{
|
|
register_IF |= 2;
|
|
gbInterruptLaunched |= 2;
|
|
if ((gbHardware & 0xa) && (IFF & 0x80))
|
|
gbInterruptWait = 1;
|
|
}
|
|
gbInt48Signal |= 4;
|
|
}
|
|
gbInt48Signal &= ~2;
|
|
|
|
gbLcdTicks += GBLCD_MODE_2_CLOCK_TICKS;
|
|
|
|
gbLcdMode = 2;
|
|
register_LY = 0x00;
|
|
|
|
}
|
|
break;
|
|
case 2:
|
|
{
|
|
|
|
// OAM being accessed mode
|
|
// next mode is OAM and VRAM in use
|
|
if ((gbScreenOn) && (register_LCDC & 0x80))
|
|
{
|
|
gbDrawSprites(false);
|
|
// Used to add a one tick delay when a window line is drawn.
|
|
//(fixes a part of Carmaggedon problem)
|
|
if((register_LCDC & 0x01 || gbCgbMode) && (register_LCDC & 0x20) &&
|
|
(gbWindowLine != -2)) {
|
|
|
|
int inUseRegister_WY = 0;
|
|
int tempgbWindowLine = gbWindowLine;
|
|
|
|
if ((tempgbWindowLine == -1) || (tempgbWindowLine>144))
|
|
{
|
|
inUseRegister_WY = oldRegister_WY;
|
|
if (register_LY>oldRegister_WY)
|
|
tempgbWindowLine = 146;
|
|
}
|
|
|
|
if(register_LY >= inUseRegister_WY) {
|
|
|
|
if (tempgbWindowLine == -1)
|
|
tempgbWindowLine = 0;
|
|
|
|
int wx = register_WX;
|
|
wx -= 7;
|
|
if (wx<0)
|
|
wx = 0;
|
|
|
|
if((wx <= 159) && (tempgbWindowLine <= 143))
|
|
{
|
|
for (int i = wx; i<300; i++)
|
|
if (gbSpeed)
|
|
gbSpritesTicks[i]+=3;
|
|
else
|
|
gbSpritesTicks[i]+=1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
gbInt48Signal &= ~7;
|
|
|
|
gbLcdTicks += GBLCD_MODE_3_CLOCK_TICKS+gbSpritesTicks[299];
|
|
gbLcdMode = 3;
|
|
}
|
|
break;
|
|
case 3:
|
|
{
|
|
// OAM and VRAM in use
|
|
// next mode is H-Blank
|
|
|
|
|
|
gbInt48Signal &= ~7;
|
|
if(register_STAT & 0x08)
|
|
{
|
|
// send LCD interrupt only if no interrupt 48h signal...
|
|
if (!(gbInt48Signal & 8))
|
|
{
|
|
register_IF |= 2;
|
|
if ((gbHardware & 0xa) && (IFF & 0x80))
|
|
gbInterruptWait = 1;
|
|
}
|
|
gbInt48Signal |= 1;
|
|
}
|
|
|
|
gbLcdTicks += GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299];
|
|
|
|
gbLcdMode = 0;
|
|
|
|
// No HDMA during HALT !
|
|
if(gbHdmaOn && (!(IFF & 0x80) || (register_IE & register_IF & 0x1f))) {
|
|
gbDoHdma();
|
|
}
|
|
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
if ((gbLcdTicksDelayed <= 0) && (gbLCDChangeHappened)) {
|
|
int framesToSkip = systemFrameSkip;
|
|
if(speedup)
|
|
framesToSkip = 9; // try 6 FPS during speedup
|
|
//gbLcdTicksDelayed = gbLcdTicks+1;
|
|
gbLCDChangeHappened = false;
|
|
switch(gbLcdModeDelayed) {
|
|
case 0:
|
|
{
|
|
// H-Blank
|
|
|
|
memset(gbSCYLine,gbSCYLine[299],sizeof(gbSCYLine));
|
|
memset(gbSCXLine,gbSCXLine[299],sizeof(gbSCXLine));
|
|
memset(gbBgpLine,gbBgpLine[299],sizeof(gbBgpLine));
|
|
memset(gbObp0Line,gbObp0Line[299],sizeof(gbObp0Line));
|
|
memset(gbObp1Line,gbObp1Line[299],sizeof(gbObp1Line));
|
|
memset(gbSpritesTicks,gbSpritesTicks[299],sizeof(gbSpritesTicks));
|
|
|
|
if (gbWindowLine <0)
|
|
oldRegister_WY = register_WY;
|
|
// check if we reached the V-Blank period
|
|
if(register_LY == 144) {
|
|
// Yes, V-Blank
|
|
// set the LY increment counter
|
|
|
|
if(register_LCDC & 0x80) {
|
|
if (gbHardware & 0xa)
|
|
{
|
|
|
|
register_IF |= 1; // V-Blank interrupt
|
|
gbInterruptLaunched |=1;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
gbLcdTicksDelayed += GBLCD_MODE_1_CLOCK_TICKS;
|
|
gbLcdModeDelayed = 1;
|
|
|
|
gbFrameCount++;
|
|
systemFrame();
|
|
|
|
if((gbFrameCount % 10) == 0)
|
|
system10Frames(60);
|
|
|
|
if(gbFrameCount >= 60) {
|
|
u32 currentTime = systemGetClock();
|
|
if(currentTime != gbLastTime)
|
|
systemShowSpeed(100000/(currentTime - gbLastTime));
|
|
else
|
|
systemShowSpeed(0);
|
|
gbLastTime = currentTime;
|
|
gbFrameCount = 0;
|
|
}
|
|
|
|
if(systemReadJoypads()) {
|
|
// read joystick
|
|
if(gbSgbMode && gbSgbMultiplayer) {
|
|
if(gbSgbFourPlayers) {
|
|
gbJoymask[0] = systemReadJoypad(0);
|
|
gbJoymask[1] = systemReadJoypad(1);
|
|
gbJoymask[2] = systemReadJoypad(2);
|
|
gbJoymask[3] = systemReadJoypad(3);
|
|
} else {
|
|
gbJoymask[0] = systemReadJoypad(0);
|
|
gbJoymask[1] = systemReadJoypad(1);
|
|
}
|
|
} else {
|
|
gbJoymask[0] = systemReadJoypad(-1);
|
|
}
|
|
}
|
|
int newmask = gbJoymask[0] & 255;
|
|
|
|
if(gbRomType == 0x22) {
|
|
systemUpdateMotionSensor();
|
|
}
|
|
|
|
if(newmask)
|
|
{
|
|
gbMemory[0xff0f] = register_IF |= 16;
|
|
}
|
|
|
|
|
|
newmask = (gbJoymask[0] >> 10);
|
|
|
|
speedup = (newmask & 1) ? true : false;
|
|
gbCapture = (newmask & 2) ? true : false;
|
|
|
|
if(gbCapture && !gbCapturePrevious) {
|
|
gbCaptureNumber++;
|
|
systemScreenCapture(gbCaptureNumber);
|
|
}
|
|
gbCapturePrevious = gbCapture;
|
|
|
|
if(gbFrameSkipCount >= framesToSkip) {
|
|
|
|
if(!gbSgbMask)
|
|
{
|
|
if (gbBorderOn)
|
|
gbSgbRenderBorder();
|
|
//if (gbScreenOn)
|
|
systemDrawScreen();
|
|
if(systemPauseOnFrame())
|
|
ticksToStop = 0;
|
|
}
|
|
gbFrameSkipCount = 0;
|
|
} else
|
|
gbFrameSkipCount++;
|
|
|
|
} else {
|
|
// go the the OAM being accessed mode
|
|
gbLcdTicksDelayed += GBLCD_MODE_2_CLOCK_TICKS;
|
|
gbLcdModeDelayed = 2;
|
|
gbInt48Signal &= ~3;
|
|
}
|
|
}
|
|
break;
|
|
case 1:
|
|
{
|
|
// V-Blank
|
|
// next mode is OAM being accessed mode
|
|
|
|
// gbScreenOn = true;
|
|
|
|
oldRegister_WY = register_WY;
|
|
|
|
gbLcdTicksDelayed += GBLCD_MODE_2_CLOCK_TICKS;
|
|
gbLcdModeDelayed = 2;
|
|
|
|
// reset the window line
|
|
gbWindowLine = -1;
|
|
}
|
|
break;
|
|
case 2:
|
|
{
|
|
// OAM being accessed mode
|
|
// next mode is OAM and VRAM in use
|
|
gbLcdTicksDelayed += GBLCD_MODE_3_CLOCK_TICKS+gbSpritesTicks[299];
|
|
gbLcdModeDelayed = 3;
|
|
}
|
|
break;
|
|
case 3:
|
|
{
|
|
|
|
// OAM and VRAM in use
|
|
// next mode is H-Blank
|
|
if((register_LY < 144) && (register_LCDC & 0x80) && gbScreenOn) {
|
|
if(!gbSgbMask) {
|
|
if(gbFrameSkipCount >= framesToSkip) {
|
|
if (!gbBlackScreen)
|
|
{
|
|
gbRenderLine();
|
|
gbDrawSprites(true);
|
|
}
|
|
else if (gbBlackScreen)
|
|
{
|
|
u16 color = gbColorOption ? gbColorFilter[0] : 0;
|
|
if (!gbCgbMode)
|
|
color = gbColorOption ? gbColorFilter[gbPalette[3] & 0x7FFF] : gbPalette[3] & 0x7FFF;
|
|
for(int i = 0; i < 160; i++)
|
|
{
|
|
gbLineMix[i] = color;
|
|
gbLineBuffer[i] = 0;
|
|
}
|
|
}
|
|
gbDrawLine();
|
|
}
|
|
}
|
|
}
|
|
gbLcdTicksDelayed += GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299];
|
|
gbLcdModeDelayed = 0;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((gbLcdLYIncrementTicksDelayed <= 0) && (gbLYChangeHappened == true))
|
|
{
|
|
|
|
gbLYChangeHappened = false;
|
|
|
|
if (!((gbLcdMode != 1) && (register_LY==0)))
|
|
{
|
|
{
|
|
gbInt48Signal &= ~8;
|
|
gbCompareLYToLYC();
|
|
if ((gbInt48Signal == 8) && (!((register_LY == 0) && (gbHardware & 1))))
|
|
gbInterruptLaunched |= 2;
|
|
if ((gbHardware & (gbSpeed ? 8 : 2)) && (register_LY==0) && ((register_STAT & 0x44) == 0x44) && (gbLcdLYIncrementTicksDelayed==0))
|
|
{
|
|
gbInterruptWait = 1;
|
|
|
|
}
|
|
}
|
|
}
|
|
gbLcdLYIncrementTicksDelayed += GBLY_INCREMENT_CLOCK_TICKS;
|
|
|
|
if (gbLcdModeDelayed == 1)
|
|
{
|
|
|
|
if(register_LY == 153)
|
|
gbLcdLYIncrementTicksDelayed -= GBLY_INCREMENT_CLOCK_TICKS - gbLine99Ticks;
|
|
else if(register_LY == 0)
|
|
gbLcdLYIncrementTicksDelayed += GBLY_INCREMENT_CLOCK_TICKS - gbLine99Ticks;
|
|
}
|
|
gbMemory[0xff0f] = register_IF;
|
|
gbMemory[0xff41] = register_STAT;
|
|
}
|
|
}
|
|
gbMemory[0xff0f] = register_IF;
|
|
gbMemory[0xff41] = register_STAT = (register_STAT & 0xfc) | gbLcdModeDelayed;
|
|
}
|
|
else
|
|
{
|
|
|
|
// Used to update the screen with white lines when it's off.
|
|
// (it looks strange, but it's kinda accurate :p)
|
|
// You can try the Mario Demo Vx.x for exemple
|
|
// (check the bottom 2 lines while moving)
|
|
if (!gbWhiteScreen)
|
|
{
|
|
gbScreenTicks -= clockTicks;
|
|
gbLcdLYIncrementTicks -= clockTicks;
|
|
while (gbLcdLYIncrementTicks <=0)
|
|
{
|
|
register_LY = ((register_LY+1)%154);
|
|
gbLcdLYIncrementTicks+=GBLY_INCREMENT_CLOCK_TICKS;
|
|
}
|
|
if (gbScreenTicks <= 0)
|
|
{
|
|
gbWhiteScreen = 1;
|
|
u8 register_LYLcdOff = ((register_LY+154)%154);
|
|
for (register_LY=0;register_LY <= 0x90;register_LY++)
|
|
{
|
|
u16 color = gbColorOption ? gbColorFilter[0x7FFF] : 0x7FFF;
|
|
if (!gbCgbMode)
|
|
color = gbColorOption ? gbColorFilter[gbPalette[0] & 0x7FFF] : gbPalette[0] & 0x7FFF;
|
|
for(int i = 0; i < 160; i++)
|
|
{
|
|
gbLineMix[i] = color;
|
|
gbLineBuffer[i] = 0;
|
|
}
|
|
gbDrawLine();
|
|
}
|
|
register_LY = register_LYLcdOff;
|
|
}
|
|
}
|
|
|
|
if (gbWhiteScreen)
|
|
{
|
|
gbLcdLYIncrementTicks -= clockTicks;
|
|
|
|
while (gbLcdLYIncrementTicks <=0)
|
|
{
|
|
register_LY = ((register_LY+1)%154);
|
|
gbLcdLYIncrementTicks+=GBLY_INCREMENT_CLOCK_TICKS;
|
|
if (register_LY<144)
|
|
{
|
|
|
|
u16 color = gbColorOption ? gbColorFilter[0x7FFF] : 0x7FFF;
|
|
if (!gbCgbMode)
|
|
color = gbColorOption ? gbColorFilter[gbPalette[0] & 0x7FFF] : gbPalette[0] & 0x7FFF;
|
|
for(int i = 0; i < 160; i++)
|
|
{
|
|
gbLineMix[i] = color;
|
|
gbLineBuffer[i] = 0;
|
|
}
|
|
gbDrawLine();
|
|
}
|
|
else if ((register_LY==144) && (!systemFrameSkip))
|
|
{
|
|
int framesToSkip = systemFrameSkip;
|
|
if(speedup)
|
|
framesToSkip = 9; // try 6 FPS during speedup
|
|
if((gbFrameSkipCount >= framesToSkip) || (gbWhiteScreen == 1)) {
|
|
gbWhiteScreen = 2;
|
|
|
|
if(!gbSgbMask)
|
|
{
|
|
if (gbBorderOn)
|
|
gbSgbRenderBorder();
|
|
//if (gbScreenOn)
|
|
systemDrawScreen();
|
|
if(systemPauseOnFrame())
|
|
ticksToStop = 0;
|
|
}
|
|
}
|
|
if(systemReadJoypads()) {
|
|
// read joystick
|
|
if(gbSgbMode && gbSgbMultiplayer) {
|
|
if(gbSgbFourPlayers) {
|
|
gbJoymask[0] = systemReadJoypad(0);
|
|
gbJoymask[1] = systemReadJoypad(1);
|
|
gbJoymask[2] = systemReadJoypad(2);
|
|
gbJoymask[3] = systemReadJoypad(3);
|
|
} else {
|
|
gbJoymask[0] = systemReadJoypad(0);
|
|
gbJoymask[1] = systemReadJoypad(1);
|
|
}
|
|
} else {
|
|
gbJoymask[0] = systemReadJoypad(-1);
|
|
}
|
|
}
|
|
gbFrameCount++;
|
|
|
|
systemFrame();
|
|
|
|
if((gbFrameCount % 10) == 0)
|
|
system10Frames(60);
|
|
|
|
if(gbFrameCount >= 60) {
|
|
u32 currentTime = systemGetClock();
|
|
if(currentTime != gbLastTime)
|
|
systemShowSpeed(100000/(currentTime - gbLastTime));
|
|
else
|
|
systemShowSpeed(0);
|
|
gbLastTime = currentTime;
|
|
gbFrameCount = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
gbMemory[0xff41] = register_STAT;
|
|
|
|
// serial emulation
|
|
if(gbSerialOn) {
|
|
#ifdef OLD_GB_LINK
|
|
if(linkConnected) {
|
|
gbSerialTicks -= clockTicks;
|
|
|
|
while(gbSerialTicks <= 0) {
|
|
// increment number of shifted bits
|
|
gbSerialBits++;
|
|
linkProc();
|
|
if(gbSerialOn && (gbMemory[0xff02] & 1)) {
|
|
if(gbSerialBits == 8) {
|
|
gbSerialBits = 0;
|
|
gbMemory[0xff01] = 0xff;
|
|
gbMemory[0xff02] &= 0x7f;
|
|
gbSerialOn = 0;
|
|
gbMemory[0xff0f] = register_IF |= 8;
|
|
gbSerialTicks = 0;
|
|
}
|
|
}
|
|
gbSerialTicks += GBSERIAL_CLOCK_TICKS;
|
|
}
|
|
} else {
|
|
#endif
|
|
if(gbMemory[0xff02] & 1) {
|
|
gbSerialTicks -= clockTicks;
|
|
|
|
// overflow
|
|
while(gbSerialTicks <= 0) {
|
|
// shift serial byte to right and put a 1 bit in its place
|
|
// gbMemory[0xff01] = 0x80 | (gbMemory[0xff01]>>1);
|
|
// increment number of shifted bits
|
|
gbSerialBits++;
|
|
if(gbSerialBits == 8) {
|
|
// end of transmission
|
|
if(gbSerialFunction) // external device
|
|
gbMemory[0xff01] = gbSerialFunction(gbMemory[0xff01]);
|
|
else
|
|
gbMemory[0xff01] = 0xff;
|
|
gbSerialTicks = 0;
|
|
gbMemory[0xff02] &= 0x7f;
|
|
gbSerialOn = 0;
|
|
gbMemory[0xff0f] = register_IF |= 8;
|
|
gbSerialBits = 0;
|
|
} else
|
|
gbSerialTicks += GBSERIAL_CLOCK_TICKS;
|
|
}
|
|
}
|
|
#ifdef OLD_GB_LINK
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
soundTicks -= clockTicks;
|
|
if ( !gbSpeed )
|
|
soundTicks -= clockTicks;
|
|
|
|
while(soundTicks < 0) {
|
|
soundTicks += SOUND_CLOCK_TICKS;
|
|
|
|
gbSoundTick();
|
|
}
|
|
|
|
|
|
// timer emulation
|
|
|
|
if(gbTimerOn) {
|
|
gbTimerTicks= ((gbInternalTimer) & gbTimerMask[gbTimerMode])+1-clockTicks;
|
|
|
|
while(gbTimerTicks <= 0) {
|
|
register_TIMA++;
|
|
// timer overflow!
|
|
if((register_TIMA & 0xff) == 0) {
|
|
// reload timer modulo
|
|
register_TIMA = register_TMA;
|
|
// flag interrupt
|
|
gbMemory[0xff0f] = register_IF |= 4;
|
|
}
|
|
gbTimerTicks += gbTimerClockTicks;
|
|
}
|
|
gbTimerOnChange = false;
|
|
gbTimerModeChange = false;
|
|
|
|
gbMemory[0xff05] = register_TIMA;
|
|
|
|
}
|
|
|
|
gbInternalTimer -= clockTicks;
|
|
while (gbInternalTimer<0)
|
|
gbInternalTimer+=0x100;
|
|
|
|
clockTicks = 0;
|
|
|
|
if (gbIntBreak == 1)
|
|
{
|
|
gbIntBreak = 0;
|
|
if ((register_IE & register_IF & gbInterruptLaunched & 0x3) &&
|
|
((IFF & 0x81) == 1) && (!gbInterruptWait) && (execute))
|
|
{
|
|
gbIntBreak = 2;
|
|
PC.W = oldPCW;
|
|
execute = false;
|
|
gbOldClockTicks = 0;
|
|
}
|
|
if (gbOldClockTicks)
|
|
{
|
|
clockTicks = gbOldClockTicks;
|
|
gbOldClockTicks = 0;
|
|
goto gbRedoLoop;
|
|
}
|
|
}
|
|
|
|
// Executes the opcode(s), and apply the instruction's remaining clockTicks (if any).
|
|
if (execute)
|
|
{
|
|
switch(opcode1) {
|
|
case 0xCB:
|
|
// extended opcode
|
|
switch(opcode2) {
|
|
#include "gbCodesCB.h"
|
|
}
|
|
break;
|
|
#include "gbCodes.h"
|
|
}
|
|
execute = false;
|
|
|
|
if (clockTicks)
|
|
{
|
|
gbDmaTicks += clockTicks;
|
|
clockTicks = 0;
|
|
}
|
|
}
|
|
|
|
if (gbDmaTicks)
|
|
{
|
|
clockTicks = gbGetNextEvent(gbDmaTicks);
|
|
|
|
if (clockTicks<=gbDmaTicks)
|
|
gbDmaTicks -= clockTicks;
|
|
else
|
|
{
|
|
clockTicks = gbDmaTicks;
|
|
gbDmaTicks = 0;
|
|
}
|
|
|
|
goto gbRedoLoop;
|
|
}
|
|
|
|
|
|
// Remove the 'if an IE is pending' flag if IE has finished being executed.
|
|
if ((IFF & 0x40) && !(IFF & 0x30))
|
|
IFF &= 0x81;
|
|
|
|
|
|
|
|
if ((register_IE & register_IF & 0x1f) && (IFF & 0x81) && (!gbInterruptWait))
|
|
{
|
|
|
|
if (IFF & 1)
|
|
{
|
|
// Add 5 ticks for the interrupt execution time
|
|
gbDmaTicks += 5;
|
|
|
|
if (gbIntBreak == 2)
|
|
{
|
|
gbDmaTicks--;
|
|
gbIntBreak = 0;
|
|
}
|
|
|
|
|
|
if(register_IF & register_IE & 1)
|
|
gbVblank_interrupt();
|
|
else if(register_IF & register_IE & 2)
|
|
gbLcd_interrupt();
|
|
else if(register_IF & register_IE & 4)
|
|
gbTimer_interrupt();
|
|
else if(register_IF & register_IE & 8)
|
|
gbSerial_interrupt();
|
|
else if(register_IF & register_IE & 16)
|
|
gbJoypad_interrupt();
|
|
}
|
|
|
|
IFF &= ~0x81;
|
|
}
|
|
|
|
if (IFF & 0x08)
|
|
IFF &=~0x79;
|
|
|
|
// Used to apply the interrupt's execution time.
|
|
if (gbDmaTicks)
|
|
{
|
|
clockTicks = gbGetNextEvent(gbDmaTicks);
|
|
|
|
if (clockTicks<=gbDmaTicks)
|
|
gbDmaTicks -= clockTicks;
|
|
else
|
|
{
|
|
clockTicks = gbDmaTicks;
|
|
gbDmaTicks = 0;
|
|
}
|
|
goto gbRedoLoop;
|
|
}
|
|
|
|
|
|
gbBlackScreen = false;
|
|
|
|
if((ticksToStop <= 0)) {
|
|
if(!(register_LCDC & 0x80)) {
|
|
if(systemReadJoypads()) {
|
|
// read joystick
|
|
if(gbSgbMode && gbSgbMultiplayer) {
|
|
if(gbSgbFourPlayers) {
|
|
gbJoymask[0] = systemReadJoypad(0);
|
|
gbJoymask[1] = systemReadJoypad(1);
|
|
gbJoymask[2] = systemReadJoypad(2);
|
|
gbJoymask[3] = systemReadJoypad(3);
|
|
} else {
|
|
gbJoymask[0] = systemReadJoypad(0);
|
|
gbJoymask[1] = systemReadJoypad(1);
|
|
}
|
|
} else {
|
|
gbJoymask[0] = systemReadJoypad(-1);
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
struct EmulatedSystem GBSystem = {
|
|
// emuMain
|
|
gbEmulate,
|
|
// emuReset
|
|
gbReset,
|
|
// emuCleanUp
|
|
gbCleanUp,
|
|
// emuReadBattery
|
|
gbReadBatteryFile,
|
|
// emuWriteBattery
|
|
gbWriteBatteryFile,
|
|
// emuReadState
|
|
gbReadSaveState,
|
|
// emuWriteState
|
|
gbWriteSaveState,
|
|
// emuReadMemState
|
|
gbReadMemSaveState,
|
|
// emuWriteMemState
|
|
gbWriteMemSaveState,
|
|
// emuWritePNG
|
|
gbWritePNGFile,
|
|
// emuWriteBMP
|
|
gbWriteBMPFile,
|
|
// emuUpdateCPSR
|
|
NULL,
|
|
// emuHasDebugger
|
|
false,
|
|
// emuCount
|
|
#ifdef FINAL_VERSION
|
|
70000/4,
|
|
#else
|
|
1000,
|
|
#endif
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Nintendo Wii/Gamecube Port Additions
|
|
*
|
|
* Duplicate versions of save functions above, using memory
|
|
* I want to kill whoever wrote so many stupid functions, and did so without
|
|
* doing it memory-based
|
|
* Tantric - October 2008
|
|
***************************************************************************/
|
|
|
|
void swap_endian_32(void* ptr, int length) {
|
|
u8* byteptr = (u8*)ptr;
|
|
u8 b1, b2, b3, b4;
|
|
for (int i=0; i<length; i+=4) {
|
|
b1 = byteptr[i+0];
|
|
b2 = byteptr[i+1];
|
|
b3 = byteptr[i+2];
|
|
b4 = byteptr[i+3];
|
|
byteptr[i+0] = b4;
|
|
byteptr[i+1] = b3;
|
|
byteptr[i+2] = b2;
|
|
byteptr[i+3] = b1;
|
|
}
|
|
}
|
|
|
|
int MemgbWriteSaveMBC1(char * membuffer) {
|
|
if (gbRam) {
|
|
memcpy(membuffer, gbRam, (gbRamSizeMask + 1));
|
|
return (gbRamSizeMask + 1);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int MemgbWriteSaveMBC2(char * membuffer) {
|
|
if (gbRam) {
|
|
memcpy(membuffer, gbRam, 512);
|
|
return 512;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int MemgbWriteSaveMBC3(char * membuffer, bool extendedSave) {
|
|
int offset = 0;
|
|
if (gbRam || extendedSave) {
|
|
if (gbRam) {
|
|
memcpy(membuffer, gbRam, (gbRamSizeMask + 1));
|
|
offset += (gbRamSizeMask + 1);
|
|
}
|
|
|
|
if (extendedSave)
|
|
{
|
|
memcpy(membuffer+offset, &gbDataMBC3.mapperSeconds, (10 * sizeof(int)
|
|
+ sizeof(time_t)));
|
|
swap_endian_32(membuffer+offset, sizeof(int) * 10 + sizeof(time_t));
|
|
offset += (10 * sizeof(int) + sizeof(time_t));
|
|
}
|
|
}
|
|
return offset;
|
|
}
|
|
|
|
int MemgbWriteSaveMBC5(char * membuffer) {
|
|
if (gbRam) {
|
|
memcpy(membuffer, gbRam, (gbRamSizeMask + 1));
|
|
return (gbRamSizeMask + 1);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int MemgbWriteSaveMBC7(char * membuffer) {
|
|
if (gbRam) {
|
|
memcpy(membuffer, gbRam, 256);
|
|
return 256;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int MemgbWriteSaveTAMA5(char * membuffer, bool extendedSave) {
|
|
int offset = 0;
|
|
|
|
if (gbRam)
|
|
{
|
|
memcpy(membuffer, gbRam, (gbRamSizeMask + 1));
|
|
offset += (gbRamSizeMask + 1);
|
|
}
|
|
|
|
memcpy(membuffer+offset, gbTAMA5ram, (gbTAMA5ramSize));
|
|
offset += (gbTAMA5ramSize);
|
|
|
|
if (extendedSave)
|
|
{
|
|
memcpy(membuffer+offset, &gbDataTAMA5.mapperSeconds, (14 * sizeof(int) + sizeof(time_t)));
|
|
swap_endian_32(membuffer+offset, sizeof(int) * 14 + sizeof(time_t));
|
|
offset += (14 * sizeof(int) + sizeof(time_t));
|
|
}
|
|
return offset;
|
|
}
|
|
|
|
int MemgbWriteSaveMMM01(char * membuffer) {
|
|
if (gbRam) {
|
|
memcpy(membuffer, gbRam, (gbRamSizeMask + 1));
|
|
return (gbRamSizeMask + 1);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
bool MemgbReadSaveMBC1(char * membuffer, int read) {
|
|
if (gbRam)
|
|
{
|
|
if (read != (gbRamSizeMask + 1))
|
|
return false;
|
|
else
|
|
memcpy(gbRam, membuffer, read);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool MemgbReadSaveMBC2(char * membuffer, int read) {
|
|
if (gbRam)
|
|
{
|
|
if (read != 512)
|
|
return false;
|
|
else
|
|
memcpy(gbRam, membuffer, read);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool MemgbReadSaveMBC3(char * membuffer, int read) {
|
|
int offset = 0;
|
|
|
|
if (gbRam)
|
|
{
|
|
if(read < (gbRamSizeMask + 1))
|
|
return false;
|
|
memcpy(gbRam, membuffer, (gbRamSizeMask + 1));
|
|
offset += (gbRamSizeMask + 1);
|
|
}
|
|
|
|
int gbRomType = gbRom[0x147];
|
|
|
|
if ((gbRomType == 0xf) || (gbRomType == 0x10))
|
|
{
|
|
if((uint)read < (offset + sizeof(int) * 10 + sizeof(time_t)))
|
|
return false;
|
|
memcpy(&gbDataMBC3.mapperSeconds, membuffer+offset, sizeof(int) * 10 + sizeof(time_t));
|
|
|
|
bool zero = gbDataMBC3.mapperSeconds || gbDataMBC3.mapperMinutes || gbDataMBC3.mapperHours;
|
|
// If all values are zero, it's not possible to determine the byte order - assume little endian
|
|
if (!zero && gbDataMBC3.mapperSeconds <= 60 && gbDataMBC3.mapperMinutes <= 60 && gbDataMBC3.mapperHours <= 24) {
|
|
// Big endian format (VBA-GX 2.3.0 and lower)
|
|
} else {
|
|
// Little endian format (VBA/VBA-M on x86)
|
|
swap_endian_32(&gbDataMBC3.mapperSeconds, sizeof(int) * 10 + sizeof(time_t));
|
|
if (gbDataMBC3.mapperSeconds > 60) {
|
|
// Reset the clock
|
|
memset(&gbDataMBC3.mapperSeconds, 0, sizeof(int) * 10 + sizeof(time_t));
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool MemgbReadSaveMBC5(char * membuffer, int read) {
|
|
if (gbRam)
|
|
{
|
|
if (read != (gbRamSizeMask + 1))
|
|
return false;
|
|
else
|
|
memcpy(gbRam, membuffer, read);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool MemgbReadSaveMBC7(char * membuffer, int read) {
|
|
if (gbRam)
|
|
{
|
|
if (read != 256)
|
|
return false;
|
|
else
|
|
memcpy(gbRam, membuffer, read);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool MemgbReadSaveTAMA5(char * membuffer, int read) {
|
|
if (gbRam)
|
|
{
|
|
if (gbRamSizeMask + gbTAMA5ramSize + 1)
|
|
return false;
|
|
|
|
memcpy(gbRam, membuffer, (gbRamSizeMask + 1));
|
|
int offset = (gbRamSizeMask + 1);
|
|
memcpy(&gbDataTAMA5.mapperSeconds, membuffer+offset, sizeof(int) * 14 + sizeof(time_t));
|
|
|
|
swap_endian_32(&gbDataTAMA5.mapperSeconds, sizeof(int) * 14 + sizeof(time_t));
|
|
|
|
bool zero = gbDataTAMA5.mapperSeconds || gbDataTAMA5.mapperMinutes || gbDataTAMA5.mapperHours;
|
|
// If all values are zero, it's not possible to determine the byte order - assume little endian
|
|
if (!zero && gbDataTAMA5.mapperSeconds <= 60 && gbDataTAMA5.mapperMinutes <= 60 && gbDataTAMA5.mapperHours <= 24) {
|
|
// Big endian format (VBA-GX 2.3.0 and lower)
|
|
} else {
|
|
// Little endian format (VBA/VBA-M on x86)
|
|
swap_endian_32(&gbDataTAMA5.mapperSeconds, sizeof(int) * 14 + sizeof(time_t));
|
|
if (gbDataTAMA5.mapperSeconds > 60) {
|
|
// Reset the clock
|
|
memset(&gbDataMBC3.mapperSeconds, 0, sizeof(int) * 14 + sizeof(time_t));
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool MemgbReadSaveMMM01(char * membuffer, int read) {
|
|
if (gbRam)
|
|
{
|
|
if (read != (gbRamSizeMask + 1))
|
|
return false;
|
|
else
|
|
memcpy(gbRam, membuffer, read);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int MemgbWriteBatteryFile(char * membuffer)
|
|
{
|
|
int result = 0;
|
|
if(gbBattery)
|
|
{
|
|
int type = gbRom[0x147];
|
|
bool extendedSave = true;
|
|
|
|
switch(type)
|
|
{
|
|
case 0x03:
|
|
result = MemgbWriteSaveMBC1(membuffer);
|
|
break;
|
|
case 0x06:
|
|
result = MemgbWriteSaveMBC2(membuffer);
|
|
break;
|
|
case 0x0d:
|
|
result = MemgbWriteSaveMMM01(membuffer);
|
|
break;
|
|
case 0x0f:
|
|
case 0x10:
|
|
result = MemgbWriteSaveMBC3(membuffer, extendedSave);
|
|
break;
|
|
case 0x13:
|
|
case 0xfc:
|
|
result = MemgbWriteSaveMBC3(membuffer, false);
|
|
case 0x1b:
|
|
case 0x1e:
|
|
result = MemgbWriteSaveMBC5(membuffer);
|
|
break;
|
|
case 0x22:
|
|
result = MemgbWriteSaveMBC7(membuffer);
|
|
break;
|
|
case 0xfd:
|
|
result = MemgbWriteSaveTAMA5(membuffer, extendedSave);
|
|
break;
|
|
case 0xff:
|
|
result = MemgbWriteSaveMBC1(membuffer);
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool MemgbReadBatteryFile(char * membuffer, int read)
|
|
{
|
|
bool res = false;
|
|
if (gbBattery) {
|
|
int type = gbRom[0x147];
|
|
switch (type) {
|
|
case 0x03:
|
|
res = MemgbReadSaveMBC1(membuffer, read);
|
|
break;
|
|
case 0x06:
|
|
res = MemgbReadSaveMBC2(membuffer, read);
|
|
break;
|
|
case 0x0d:
|
|
res = MemgbReadSaveMMM01(membuffer, read);
|
|
break;
|
|
case 0x0f:
|
|
case 0x10:
|
|
res = MemgbReadSaveMBC3(membuffer, read);
|
|
if (!res) {
|
|
time(&gbDataMBC3.mapperLastTime);
|
|
struct tm *lt;
|
|
lt = localtime(&gbDataMBC3.mapperLastTime);
|
|
gbDataMBC3.mapperSeconds = lt->tm_sec;
|
|
gbDataMBC3.mapperMinutes = lt->tm_min;
|
|
gbDataMBC3.mapperHours = lt->tm_hour;
|
|
gbDataMBC3.mapperDays = lt->tm_yday & 255;
|
|
gbDataMBC3.mapperControl = (gbDataMBC3.mapperControl & 0xfe)
|
|
| (lt->tm_yday > 255 ? 1 : 0);
|
|
res = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case 0x13:
|
|
case 0xfc:
|
|
res = MemgbReadSaveMBC3(membuffer, read);
|
|
break;
|
|
case 0x1b:
|
|
case 0x1e:
|
|
res = MemgbReadSaveMBC5(membuffer, read);
|
|
break;
|
|
case 0x22:
|
|
res = MemgbReadSaveMBC7(membuffer, read);
|
|
break;
|
|
case 0xfd:
|
|
res = MemgbReadSaveTAMA5(membuffer, read);
|
|
if (!res) {
|
|
u8 gbDaysinMonth[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30,
|
|
31, 30, 31 };
|
|
time(&gbDataTAMA5.mapperLastTime);
|
|
struct tm *lt;
|
|
lt = localtime(&gbDataTAMA5.mapperLastTime);
|
|
gbDataTAMA5.mapperSeconds = lt->tm_sec;
|
|
gbDataTAMA5.mapperMinutes = lt->tm_min;
|
|
gbDataTAMA5.mapperHours = lt->tm_hour;
|
|
gbDataTAMA5.mapperDays = 1;
|
|
gbDataTAMA5.mapperMonths = 1;
|
|
gbDataTAMA5.mapperYears = 1970;
|
|
int days = lt->tm_yday + 365 * 3;
|
|
while (days) {
|
|
gbDataTAMA5.mapperDays++;
|
|
days--;
|
|
if (gbDataTAMA5.mapperDays
|
|
> gbDaysinMonth[gbDataTAMA5.mapperMonths - 1]) {
|
|
gbDataTAMA5.mapperDays = 1;
|
|
gbDataTAMA5.mapperMonths++;
|
|
if (gbDataTAMA5.mapperMonths > 12) {
|
|
gbDataTAMA5.mapperMonths = 1;
|
|
gbDataTAMA5.mapperYears++;
|
|
if ((gbDataTAMA5.mapperYears & 3) == 0)
|
|
gbDaysinMonth[1] = 29;
|
|
else
|
|
gbDaysinMonth[1] = 28;
|
|
}
|
|
}
|
|
}
|
|
gbDataTAMA5.mapperControl = (gbDataTAMA5.mapperControl & 0xfe)
|
|
| (lt->tm_yday > 255 ? 1 : 0);
|
|
res = false;
|
|
break;
|
|
}
|
|
break;
|
|
case 0xff:
|
|
res = MemgbReadSaveMBC1(membuffer, read);
|
|
break;
|
|
}
|
|
}
|
|
systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED;
|
|
return res;
|
|
}
|