diff --git a/src/agb/GBA.cpp b/src/agb/GBA.cpp new file mode 100644 index 00000000..82691d19 --- /dev/null +++ b/src/agb/GBA.cpp @@ -0,0 +1,3983 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2005-2006 Forgotten and the VBA development team +// Copyright (C) VBA-M development team +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include +#include +#include +#include +#include +#include "GBA.h" +#include "GBAcpu.h" +#include "GBAinline.h" +#include "../Globals.h" +#include "GBAGFX.h" +#include "../EEprom.h" +#include "../Flash.h" +#include "../Sound.h" +#include "../Sram.h" +#include "../bios.h" +#include "../Cheats.h" +#include "../NLS.h" +#include "../elf.h" +#include "../Util.h" +#include "../Port.h" +#include "agbprint.h" +#ifdef PROFILING +#include "prof/prof.h" +#endif + +#ifdef __GNUC__ +#define _stricmp strcasecmp +#endif + + +extern int emulating; +#ifdef LINK_EMULATION +extern int linktime; +extern void StartLink(u16); +extern void StartJOYLink(u16); +extern void StartGPLink(u16); +extern void LinkSSend(u16); +extern void LinkUpdate(int); +extern int linktime2; +#endif +int SWITicks = 0; +int IRQTicks = 0; + +u32 mastercode = 0; +int layerEnableDelay = 0; +bool busPrefetch = false; +bool busPrefetchEnable = false; +u32 busPrefetchCount = 0; +int cpuDmaTicksToUpdate = 0; +int cpuDmaCount = 0; +bool cpuDmaHack = false; +u32 cpuDmaLast = 0; +int dummyAddress = 0; + +bool cpuBreakLoop = false; +int cpuNextEvent = 0; + +int gbaSaveType = 0; // used to remember the save type on reset +bool intState = false; +bool stopState = false; +bool holdState = false; +int holdType = 0; +bool cpuSramEnabled = true; +bool cpuFlashEnabled = true; +bool cpuEEPROMEnabled = true; +bool cpuEEPROMSensorEnabled = false; + +u32 cpuPrefetch[2]; + +int cpuTotalTicks = 0; +#ifdef PROFILING +int profilingTicks = 0; +int profilingTicksReload = 0; +static profile_segment *profilSegment = NULL; +#endif + +#ifdef BKPT_SUPPORT +u8 freezeWorkRAM[0x40000]; +u8 freezeInternalRAM[0x8000]; +u8 freezeVRAM[0x18000]; +u8 freezePRAM[0x400]; +u8 freezeOAM[0x400]; +bool debugger_last; +#endif + +int lcdTicks = (useBios && !skipBios) ? 1008 : 208; +u8 timerOnOffDelay = 0; +u16 timer0Value = 0; +bool timer0On = false; +int timer0Ticks = 0; +int timer0Reload = 0; +int timer0ClockReload = 0; +u16 timer1Value = 0; +bool timer1On = false; +int timer1Ticks = 0; +int timer1Reload = 0; +int timer1ClockReload = 0; +u16 timer2Value = 0; +bool timer2On = false; +int timer2Ticks = 0; +int timer2Reload = 0; +int timer2ClockReload = 0; +u16 timer3Value = 0; +bool timer3On = false; +int timer3Ticks = 0; +int timer3Reload = 0; +int timer3ClockReload = 0; +u32 dma0Source = 0; +u32 dma0Dest = 0; +u32 dma1Source = 0; +u32 dma1Dest = 0; +u32 dma2Source = 0; +u32 dma2Dest = 0; +u32 dma3Source = 0; +u32 dma3Dest = 0; +void (*cpuSaveGameFunc)(u32,u8) = flashSaveDecide; +void (*renderLine)() = mode0RenderLine; +bool fxOn = false; +bool windowOn = false; +int frameCount = 0; +char buffer[1024]; +FILE *out = NULL; +u32 lastTime = 0; +int count = 0; + +int capture = 0; +int capturePrevious = 0; +int captureNumber = 0; + +const int TIMER_TICKS[4] = { + 0, + 6, + 8, + 10 +}; + +const u32 objTilesAddress [3] = {0x010000, 0x014000, 0x014000}; +const u8 gamepakRamWaitState[4] = { 4, 3, 2, 8 }; +const u8 gamepakWaitState[4] = { 4, 3, 2, 8 }; +const u8 gamepakWaitState0[2] = { 2, 1 }; +const u8 gamepakWaitState1[2] = { 4, 1 }; +const u8 gamepakWaitState2[2] = { 8, 1 }; +const bool isInRom [16]= + { false, false, false, false, false, false, false, false, + true, true, true, true, true, true, false, false }; + +u8 memoryWait[16] = + { 0, 0, 2, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 0 }; +u8 memoryWait32[16] = + { 0, 0, 5, 0, 0, 1, 1, 0, 7, 7, 9, 9, 13, 13, 4, 0 }; +u8 memoryWaitSeq[16] = + { 0, 0, 2, 0, 0, 0, 0, 0, 2, 2, 4, 4, 8, 8, 4, 0 }; +u8 memoryWaitSeq32[16] = + { 0, 0, 5, 0, 0, 1, 1, 0, 5, 5, 9, 9, 17, 17, 4, 0 }; + +// The videoMemoryWait constants are used to add some waitstates +// if the opcode access video memory data outside of vblank/hblank +// It seems to happen on only one ticks for each pixel. +// Not used for now (too problematic with current code). +//const u8 videoMemoryWait[16] = +// {0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + +u8 biosProtected[4]; + +#ifdef WORDS_BIGENDIAN +bool cpuBiosSwapped = false; +#endif + +u32 myROM[] = { +0xEA000006, +0xEA000093, +0xEA000006, +0x00000000, +0x00000000, +0x00000000, +0xEA000088, +0x00000000, +0xE3A00302, +0xE1A0F000, +0xE92D5800, +0xE55EC002, +0xE28FB03C, +0xE79BC10C, +0xE14FB000, +0xE92D0800, +0xE20BB080, +0xE38BB01F, +0xE129F00B, +0xE92D4004, +0xE1A0E00F, +0xE12FFF1C, +0xE8BD4004, +0xE3A0C0D3, +0xE129F00C, +0xE8BD0800, +0xE169F00B, +0xE8BD5800, +0xE1B0F00E, +0x0000009C, +0x0000009C, +0x0000009C, +0x0000009C, +0x000001F8, +0x000001F0, +0x000000AC, +0x000000A0, +0x000000FC, +0x00000168, +0xE12FFF1E, +0xE1A03000, +0xE1A00001, +0xE1A01003, +0xE2113102, +0x42611000, +0xE033C040, +0x22600000, +0xE1B02001, +0xE15200A0, +0x91A02082, +0x3AFFFFFC, +0xE1500002, +0xE0A33003, +0x20400002, +0xE1320001, +0x11A020A2, +0x1AFFFFF9, +0xE1A01000, +0xE1A00003, +0xE1B0C08C, +0x22600000, +0x42611000, +0xE12FFF1E, +0xE92D0010, +0xE1A0C000, +0xE3A01001, +0xE1500001, +0x81A000A0, +0x81A01081, +0x8AFFFFFB, +0xE1A0000C, +0xE1A04001, +0xE3A03000, +0xE1A02001, +0xE15200A0, +0x91A02082, +0x3AFFFFFC, +0xE1500002, +0xE0A33003, +0x20400002, +0xE1320001, +0x11A020A2, +0x1AFFFFF9, +0xE0811003, +0xE1B010A1, +0xE1510004, +0x3AFFFFEE, +0xE1A00004, +0xE8BD0010, +0xE12FFF1E, +0xE0010090, +0xE1A01741, +0xE2611000, +0xE3A030A9, +0xE0030391, +0xE1A03743, +0xE2833E39, +0xE0030391, +0xE1A03743, +0xE2833C09, +0xE283301C, +0xE0030391, +0xE1A03743, +0xE2833C0F, +0xE28330B6, +0xE0030391, +0xE1A03743, +0xE2833C16, +0xE28330AA, +0xE0030391, +0xE1A03743, +0xE2833A02, +0xE2833081, +0xE0030391, +0xE1A03743, +0xE2833C36, +0xE2833051, +0xE0030391, +0xE1A03743, +0xE2833CA2, +0xE28330F9, +0xE0000093, +0xE1A00840, +0xE12FFF1E, +0xE3A00001, +0xE3A01001, +0xE92D4010, +0xE3A03000, +0xE3A04001, +0xE3500000, +0x1B000004, +0xE5CC3301, +0xEB000002, +0x0AFFFFFC, +0xE8BD4010, +0xE12FFF1E, +0xE3A0C301, +0xE5CC3208, +0xE15C20B8, +0xE0110002, +0x10222000, +0x114C20B8, +0xE5CC4208, +0xE12FFF1E, +0xE92D500F, +0xE3A00301, +0xE1A0E00F, +0xE510F004, +0xE8BD500F, +0xE25EF004, +0xE59FD044, +0xE92D5000, +0xE14FC000, +0xE10FE000, +0xE92D5000, +0xE3A0C302, +0xE5DCE09C, +0xE35E00A5, +0x1A000004, +0x05DCE0B4, +0x021EE080, +0xE28FE004, +0x159FF018, +0x059FF018, +0xE59FD018, +0xE8BD5000, +0xE169F00C, +0xE8BD5000, +0xE25EF004, +0x03007FF0, +0x09FE2000, +0x09FFC000, +0x03007FE0 +}; + +variable_desc saveGameStruct[] = { + { &DISPCNT , sizeof(u16) }, + { &DISPSTAT , sizeof(u16) }, + { &VCOUNT , sizeof(u16) }, + { &BG0CNT , sizeof(u16) }, + { &BG1CNT , sizeof(u16) }, + { &BG2CNT , sizeof(u16) }, + { &BG3CNT , sizeof(u16) }, + { &BG0HOFS , sizeof(u16) }, + { &BG0VOFS , sizeof(u16) }, + { &BG1HOFS , sizeof(u16) }, + { &BG1VOFS , sizeof(u16) }, + { &BG2HOFS , sizeof(u16) }, + { &BG2VOFS , sizeof(u16) }, + { &BG3HOFS , sizeof(u16) }, + { &BG3VOFS , sizeof(u16) }, + { &BG2PA , sizeof(u16) }, + { &BG2PB , sizeof(u16) }, + { &BG2PC , sizeof(u16) }, + { &BG2PD , sizeof(u16) }, + { &BG2X_L , sizeof(u16) }, + { &BG2X_H , sizeof(u16) }, + { &BG2Y_L , sizeof(u16) }, + { &BG2Y_H , sizeof(u16) }, + { &BG3PA , sizeof(u16) }, + { &BG3PB , sizeof(u16) }, + { &BG3PC , sizeof(u16) }, + { &BG3PD , sizeof(u16) }, + { &BG3X_L , sizeof(u16) }, + { &BG3X_H , sizeof(u16) }, + { &BG3Y_L , sizeof(u16) }, + { &BG3Y_H , sizeof(u16) }, + { &WIN0H , sizeof(u16) }, + { &WIN1H , sizeof(u16) }, + { &WIN0V , sizeof(u16) }, + { &WIN1V , sizeof(u16) }, + { &WININ , sizeof(u16) }, + { &WINOUT , sizeof(u16) }, + { &MOSAIC , sizeof(u16) }, + { &BLDMOD , sizeof(u16) }, + { &COLEV , sizeof(u16) }, + { &COLY , sizeof(u16) }, + { &DM0SAD_L , sizeof(u16) }, + { &DM0SAD_H , sizeof(u16) }, + { &DM0DAD_L , sizeof(u16) }, + { &DM0DAD_H , sizeof(u16) }, + { &DM0CNT_L , sizeof(u16) }, + { &DM0CNT_H , sizeof(u16) }, + { &DM1SAD_L , sizeof(u16) }, + { &DM1SAD_H , sizeof(u16) }, + { &DM1DAD_L , sizeof(u16) }, + { &DM1DAD_H , sizeof(u16) }, + { &DM1CNT_L , sizeof(u16) }, + { &DM1CNT_H , sizeof(u16) }, + { &DM2SAD_L , sizeof(u16) }, + { &DM2SAD_H , sizeof(u16) }, + { &DM2DAD_L , sizeof(u16) }, + { &DM2DAD_H , sizeof(u16) }, + { &DM2CNT_L , sizeof(u16) }, + { &DM2CNT_H , sizeof(u16) }, + { &DM3SAD_L , sizeof(u16) }, + { &DM3SAD_H , sizeof(u16) }, + { &DM3DAD_L , sizeof(u16) }, + { &DM3DAD_H , sizeof(u16) }, + { &DM3CNT_L , sizeof(u16) }, + { &DM3CNT_H , sizeof(u16) }, + { &TM0D , sizeof(u16) }, + { &TM0CNT , sizeof(u16) }, + { &TM1D , sizeof(u16) }, + { &TM1CNT , sizeof(u16) }, + { &TM2D , sizeof(u16) }, + { &TM2CNT , sizeof(u16) }, + { &TM3D , sizeof(u16) }, + { &TM3CNT , sizeof(u16) }, + { &P1 , sizeof(u16) }, + { &IE , sizeof(u16) }, + { &IF , sizeof(u16) }, + { &IME , sizeof(u16) }, + { &holdState, sizeof(bool) }, + { &holdType, sizeof(int) }, + { &lcdTicks, sizeof(int) }, + { &timer0On , sizeof(bool) }, + { &timer0Ticks , sizeof(int) }, + { &timer0Reload , sizeof(int) }, + { &timer0ClockReload , sizeof(int) }, + { &timer1On , sizeof(bool) }, + { &timer1Ticks , sizeof(int) }, + { &timer1Reload , sizeof(int) }, + { &timer1ClockReload , sizeof(int) }, + { &timer2On , sizeof(bool) }, + { &timer2Ticks , sizeof(int) }, + { &timer2Reload , sizeof(int) }, + { &timer2ClockReload , sizeof(int) }, + { &timer3On , sizeof(bool) }, + { &timer3Ticks , sizeof(int) }, + { &timer3Reload , sizeof(int) }, + { &timer3ClockReload , sizeof(int) }, + { &dma0Source , sizeof(u32) }, + { &dma0Dest , sizeof(u32) }, + { &dma1Source , sizeof(u32) }, + { &dma1Dest , sizeof(u32) }, + { &dma2Source , sizeof(u32) }, + { &dma2Dest , sizeof(u32) }, + { &dma3Source , sizeof(u32) }, + { &dma3Dest , sizeof(u32) }, + { &fxOn, sizeof(bool) }, + { &windowOn, sizeof(bool) }, + { &N_FLAG , sizeof(bool) }, + { &C_FLAG , sizeof(bool) }, + { &Z_FLAG , sizeof(bool) }, + { &V_FLAG , sizeof(bool) }, + { &armState , sizeof(bool) }, + { &armIrqEnable , sizeof(bool) }, + { &armNextPC , sizeof(u32) }, + { &armMode , sizeof(int) }, + { &saveType , sizeof(int) }, + { NULL, 0 } +}; + +static int romSize = 0x2000000; + +#ifdef PROFILING +void cpuProfil(profile_segment *seg) +{ + profilSegment = seg; +} + +void cpuEnableProfiling(int hz) +{ + if(hz == 0) + hz = 100; + profilingTicks = profilingTicksReload = 16777216 / hz; + profSetHertz(hz); +} +#endif + + +inline int CPUUpdateTicks() +{ + int cpuLoopTicks = lcdTicks; + + if(soundTicks < cpuLoopTicks) + cpuLoopTicks = soundTicks; + + if(timer0On && (timer0Ticks < cpuLoopTicks)) { + cpuLoopTicks = timer0Ticks; + } + if(timer1On && !(TM1CNT & 4) && (timer1Ticks < cpuLoopTicks)) { + cpuLoopTicks = timer1Ticks; + } + if(timer2On && !(TM2CNT & 4) && (timer2Ticks < cpuLoopTicks)) { + cpuLoopTicks = timer2Ticks; + } + if(timer3On && !(TM3CNT & 4) && (timer3Ticks < cpuLoopTicks)) { + cpuLoopTicks = timer3Ticks; + } +#ifdef PROFILING + if(profilingTicksReload != 0) { + if(profilingTicks < cpuLoopTicks) { + cpuLoopTicks = profilingTicks; + } + } +#endif + + if (SWITicks) { + if (SWITicks < cpuLoopTicks) + cpuLoopTicks = SWITicks; + } + + if (IRQTicks) { + if (IRQTicks < cpuLoopTicks) + cpuLoopTicks = IRQTicks; + } + + return cpuLoopTicks; +} + +void CPUUpdateWindow0() +{ + int x00 = WIN0H>>8; + int x01 = WIN0H & 255; + + if(x00 <= x01) { + for(int i = 0; i < 240; i++) { + gfxInWin0[i] = (i >= x00 && i < x01); + } + } else { + for(int i = 0; i < 240; i++) { + gfxInWin0[i] = (i >= x00 || i < x01); + } + } +} + +void CPUUpdateWindow1() +{ + int x00 = WIN1H>>8; + int x01 = WIN1H & 255; + + if(x00 <= x01) { + for(int i = 0; i < 240; i++) { + gfxInWin1[i] = (i >= x00 && i < x01); + } + } else { + for(int i = 0; i < 240; i++) { + gfxInWin1[i] = (i >= x00 || i < x01); + } + } +} + +extern u32 line0[240]; +extern u32 line1[240]; +extern u32 line2[240]; +extern u32 line3[240]; + +#define CLEAR_ARRAY(a) \ + {\ + u32 *array = (a);\ + for(int i = 0; i < 240; i++) {\ + *array++ = 0x80000000;\ + }\ + }\ + +void CPUUpdateRenderBuffers(bool force) +{ + if(!(layerEnable & 0x0100) || force) { + CLEAR_ARRAY(line0); + } + if(!(layerEnable & 0x0200) || force) { + CLEAR_ARRAY(line1); + } + if(!(layerEnable & 0x0400) || force) { + CLEAR_ARRAY(line2); + } + if(!(layerEnable & 0x0800) || force) { + CLEAR_ARRAY(line3); + } +} + +static bool CPUWriteState(gzFile gzFile) +{ + utilWriteInt(gzFile, SAVE_GAME_VERSION); + + utilGzWrite(gzFile, &rom[0xa0], 16); + + utilWriteInt(gzFile, useBios); + + utilGzWrite(gzFile, ®[0], sizeof(reg)); + + utilWriteData(gzFile, saveGameStruct); + + // new to version 0.7.1 + utilWriteInt(gzFile, stopState); + // new to version 0.8 + utilWriteInt(gzFile, IRQTicks); + + utilGzWrite(gzFile, internalRAM, 0x8000); + utilGzWrite(gzFile, paletteRAM, 0x400); + utilGzWrite(gzFile, workRAM, 0x40000); + utilGzWrite(gzFile, vram, 0x20000); + utilGzWrite(gzFile, oam, 0x400); + utilGzWrite(gzFile, pix, 4*241*162); + utilGzWrite(gzFile, ioMem, 0x400); + + eepromSaveGame(gzFile); + flashSaveGame(gzFile); + soundSaveGame(gzFile); + + cheatsSaveGame(gzFile); + + // version 1.5 + rtcSaveGame(gzFile); + + return true; +} + +bool CPUWriteState(const char *file) +{ + gzFile gzFile = utilGzOpen(file, "wb"); + + if(gzFile == NULL) { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), file); + return false; + } + + bool res = CPUWriteState(gzFile); + + utilGzClose(gzFile); + + return res; +} + +bool CPUWriteMemState(char *memory, int available) +{ + gzFile gzFile = utilMemGzOpen(memory, available, "w"); + + if(gzFile == NULL) { + return false; + } + + bool res = CPUWriteState(gzFile); + + long pos = utilGzMemTell(gzFile)+8; + + if(pos >= (available)) + res = false; + + utilGzClose(gzFile); + + return res; +} + +static bool CPUReadState(gzFile gzFile) +{ + int version = utilReadInt(gzFile); + + if(version > SAVE_GAME_VERSION || version < SAVE_GAME_VERSION_1) { + systemMessage(MSG_UNSUPPORTED_VBA_SGM, + N_("Unsupported VisualBoyAdvance save game version %d"), + version); + return false; + } + + u8 romname[17]; + + utilGzRead(gzFile, romname, 16); + + if(memcmp(&rom[0xa0], romname, 16) != 0) { + romname[16]=0; + for(int i = 0; i < 16; i++) + if(romname[i] < 32) + romname[i] = 32; + systemMessage(MSG_CANNOT_LOAD_SGM, N_("Cannot load save game for %s"), romname); + return false; + } + + bool ub = utilReadInt(gzFile) ? true : false; + + if(ub != useBios) { + 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; + } + + utilGzRead(gzFile, ®[0], sizeof(reg)); + + utilReadData(gzFile, saveGameStruct); + + if(version < SAVE_GAME_VERSION_3) + stopState = false; + else + stopState = utilReadInt(gzFile) ? true : false; + + if(version < SAVE_GAME_VERSION_4) + { + IRQTicks = 0; + intState = false; + } + else + { + IRQTicks = utilReadInt(gzFile); + if (IRQTicks>0) + intState = true; + else + { + intState = false; + IRQTicks = 0; + } + } + + utilGzRead(gzFile, internalRAM, 0x8000); + utilGzRead(gzFile, paletteRAM, 0x400); + utilGzRead(gzFile, workRAM, 0x40000); + utilGzRead(gzFile, vram, 0x20000); + utilGzRead(gzFile, oam, 0x400); + if(version < SAVE_GAME_VERSION_6) + utilGzRead(gzFile, pix, 4*240*160); + else + utilGzRead(gzFile, pix, 4*241*162); + utilGzRead(gzFile, ioMem, 0x400); + + eepromReadGame(gzFile, version); + flashReadGame(gzFile, version); + soundReadGame(gzFile, version); + + if(version > SAVE_GAME_VERSION_1) { + cheatsReadGame(gzFile, version); + } + if(version > SAVE_GAME_VERSION_6) { + rtcReadGame(gzFile); + } + + if(version <= SAVE_GAME_VERSION_7) { + u32 temp; +#define SWAP(a,b,c) \ + temp = (a);\ + (a) = (b)<<16|(c);\ + (b) = (temp) >> 16;\ + (c) = (temp) & 0xFFFF; + + SWAP(dma0Source, DM0SAD_H, DM0SAD_L); + SWAP(dma0Dest, DM0DAD_H, DM0DAD_L); + SWAP(dma1Source, DM1SAD_H, DM1SAD_L); + SWAP(dma1Dest, DM1DAD_H, DM1DAD_L); + SWAP(dma2Source, DM2SAD_H, DM2SAD_L); + SWAP(dma2Dest, DM2DAD_H, DM2DAD_L); + SWAP(dma3Source, DM3SAD_H, DM3SAD_L); + SWAP(dma3Dest, DM3DAD_H, DM3DAD_L); + } + + if(version <= SAVE_GAME_VERSION_8) { + timer0ClockReload = TIMER_TICKS[TM0CNT & 3]; + timer1ClockReload = TIMER_TICKS[TM1CNT & 3]; + timer2ClockReload = TIMER_TICKS[TM2CNT & 3]; + timer3ClockReload = TIMER_TICKS[TM3CNT & 3]; + + timer0Ticks = ((0x10000 - TM0D) << timer0ClockReload) - timer0Ticks; + timer1Ticks = ((0x10000 - TM1D) << timer1ClockReload) - timer1Ticks; + timer2Ticks = ((0x10000 - TM2D) << timer2ClockReload) - timer2Ticks; + timer3Ticks = ((0x10000 - TM3D) << timer3ClockReload) - timer3Ticks; + interp_rate(); + } + + // set pointers! + layerEnable = layerSettings & DISPCNT; + + CPUUpdateRender(); + CPUUpdateRenderBuffers(true); + CPUUpdateWindow0(); + CPUUpdateWindow1(); + gbaSaveType = 0; + switch(saveType) { + case 0: + cpuSaveGameFunc = flashSaveDecide; + break; + case 1: + cpuSaveGameFunc = sramWrite; + gbaSaveType = 1; + break; + case 2: + cpuSaveGameFunc = flashWrite; + gbaSaveType = 2; + break; + case 3: + break; + case 5: + gbaSaveType = 5; + break; + default: + systemMessage(MSG_UNSUPPORTED_SAVE_TYPE, + N_("Unsupported save type %d"), saveType); + break; + } + if(eepromInUse) + gbaSaveType = 3; + + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + if(armState) { + ARM_PREFETCH; + } else { + THUMB_PREFETCH; + } + + CPUUpdateRegister(0x204, CPUReadHalfWordQuick(0x4000204)); + + return true; +} + +bool CPUReadMemState(char *memory, int available) +{ + gzFile gzFile = utilMemGzOpen(memory, available, "r"); + + bool res = CPUReadState(gzFile); + + utilGzClose(gzFile); + + return res; +} + +bool CPUReadState(const char * file) +{ + gzFile gzFile = utilGzOpen(file, "rb"); + + if(gzFile == NULL) + return false; + + bool res = CPUReadState(gzFile); + + utilGzClose(gzFile); + + return res; +} + +bool CPUExportEepromFile(const char *fileName) +{ + if(eepromInUse) { + FILE *file = fopen(fileName, "wb"); + + if(!file) { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), + fileName); + return false; + } + + for(int i = 0; i < eepromSize;) { + for(int j = 0; j < 8; j++) { + if(fwrite(&eepromData[i+7-j], 1, 1, file) != 1) { + fclose(file); + return false; + } + } + i += 8; + } + fclose(file); + } + return true; +} + +bool CPUWriteBatteryFile(const char *fileName) +{ + if(gbaSaveType == 0) { + if(eepromInUse) + gbaSaveType = 3; + else switch(saveType) { + case 1: + gbaSaveType = 1; + break; + case 2: + gbaSaveType = 2; + break; + } + } + + if((gbaSaveType) && (gbaSaveType!=5)) { + FILE *file = fopen(fileName, "wb"); + + if(!file) { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), + fileName); + return false; + } + + // only save if Flash/Sram in use or EEprom in use + if(gbaSaveType != 3) { + if(gbaSaveType == 2) { + if(fwrite(flashSaveMemory, 1, flashSize, file) != (size_t)flashSize) { + fclose(file); + return false; + } + } else { + if(fwrite(flashSaveMemory, 1, 0x10000, file) != 0x10000) { + fclose(file); + return false; + } + } + } else { + if(fwrite(eepromData, 1, eepromSize, file) != (size_t)eepromSize) { + fclose(file); + return false; + } + } + fclose(file); + } + return true; +} + +bool CPUReadGSASnapshot(const char *fileName) +{ + int i; + FILE *file = fopen(fileName, "rb"); + + if(!file) { + systemMessage(MSG_CANNOT_OPEN_FILE, N_("Cannot open file %s"), fileName); + return false; + } + + // check file size to know what we should read + fseek(file, 0, SEEK_END); + + // long size = ftell(file); + fseek(file, 0x0, SEEK_SET); + fread(&i, 1, 4, file); + fseek(file, i, SEEK_CUR); // Skip SharkPortSave + fseek(file, 4, SEEK_CUR); // skip some sort of flag + fread(&i, 1, 4, file); // name length + fseek(file, i, SEEK_CUR); // skip name + fread(&i, 1, 4, file); // desc length + fseek(file, i, SEEK_CUR); // skip desc + fread(&i, 1, 4, file); // notes length + fseek(file, i, SEEK_CUR); // skip notes + int saveSize; + fread(&saveSize, 1, 4, file); // read length + saveSize -= 0x1c; // remove header size + char buffer[17]; + char buffer2[17]; + fread(buffer, 1, 16, file); + buffer[16] = 0; + for(i = 0; i < 16; i++) + if(buffer[i] < 32) + buffer[i] = 32; + memcpy(buffer2, &rom[0xa0], 16); + buffer2[16] = 0; + for(i = 0; i < 16; i++) + if(buffer2[i] < 32) + buffer2[i] = 32; + if(memcmp(buffer, buffer2, 16)) { + systemMessage(MSG_CANNOT_IMPORT_SNAPSHOT_FOR, + N_("Cannot import snapshot for %s. Current game is %s"), + buffer, + buffer2); + fclose(file); + return false; + } + fseek(file, 12, SEEK_CUR); // skip some flags + if(saveSize >= 65536) { + if(fread(flashSaveMemory, 1, saveSize, file) != (size_t)saveSize) { + fclose(file); + return false; + } + } else { + systemMessage(MSG_UNSUPPORTED_SNAPSHOT_FILE, + N_("Unsupported snapshot file %s"), + fileName); + fclose(file); + return false; + } + fclose(file); + CPUReset(); + return true; +} + +bool CPUWriteGSASnapshot(const char *fileName, + const char *title, + const char *desc, + const char *notes) +{ + FILE *file = fopen(fileName, "wb"); + + if(!file) { + systemMessage(MSG_CANNOT_OPEN_FILE, N_("Cannot open file %s"), fileName); + return false; + } + + u8 buffer[17]; + + utilPutDword(buffer, 0x0d); // SharkPortSave length + fwrite(buffer, 1, 4, file); + fwrite("SharkPortSave", 1, 0x0d, file); + utilPutDword(buffer, 0x000f0000); + fwrite(buffer, 1, 4, file); // save type 0x000f0000 = GBA save + utilPutDword(buffer, (u32)strlen(title)); + fwrite(buffer, 1, 4, file); // title length + fwrite(title, 1, strlen(title), file); + utilPutDword(buffer, (u32)strlen(desc)); + fwrite(buffer, 1, 4, file); // desc length + fwrite(desc, 1, strlen(desc), file); + utilPutDword(buffer, (u32)strlen(notes)); + fwrite(buffer, 1, 4, file); // notes length + fwrite(notes, 1, strlen(notes), file); + int saveSize = 0x10000; + if(gbaSaveType == 2) + saveSize = flashSize; + int totalSize = saveSize + 0x1c; + + utilPutDword(buffer, totalSize); // length of remainder of save - CRC + fwrite(buffer, 1, 4, file); + + char temp[0x2001c]; + memset(temp, 0, 28); + memcpy(temp, &rom[0xa0], 16); // copy internal name + temp[0x10] = rom[0xbe]; // reserved area (old checksum) + temp[0x11] = rom[0xbf]; // reserved area (old checksum) + temp[0x12] = rom[0xbd]; // complement check + temp[0x13] = rom[0xb0]; // maker + temp[0x14] = 1; // 1 save ? + memcpy(&temp[0x1c], flashSaveMemory, saveSize); // copy save + fwrite(temp, 1, totalSize, file); // write save + header + u32 crc = 0; + + for(int i = 0; i < totalSize; i++) { + crc += ((u32)temp[i] << (crc % 0x18)); + } + + utilPutDword(buffer, crc); + fwrite(buffer, 1, 4, file); // CRC? + + fclose(file); + return true; +} + +bool CPUImportEepromFile(const char *fileName) +{ + FILE *file = fopen(fileName, "rb"); + + if(!file) + return false; + + // check file size to know what we should read + fseek(file, 0, SEEK_END); + + long size = ftell(file); + fseek(file, 0, SEEK_SET); + if(size == 512 || size == 0x2000) { + if(fread(eepromData, 1, size, file) != (size_t)size) { + fclose(file); + return false; + } + for(int i = 0; i < size;) { + u8 tmp = eepromData[i]; + eepromData[i] = eepromData[7-i]; + eepromData[7-i] = tmp; + i++; + tmp = eepromData[i]; + eepromData[i] = eepromData[7-i]; + eepromData[7-i] = tmp; + i++; + tmp = eepromData[i]; + eepromData[i] = eepromData[7-i]; + eepromData[7-i] = tmp; + i++; + tmp = eepromData[i]; + eepromData[i] = eepromData[7-i]; + eepromData[7-i] = tmp; + i++; + i += 4; + } + } else + return false; + fclose(file); + return true; +} + +bool CPUReadBatteryFile(const char *fileName) +{ + FILE *file = fopen(fileName, "rb"); + + if(!file) + return false; + + // check file size to know what we should read + fseek(file, 0, SEEK_END); + + long size = ftell(file); + fseek(file, 0, SEEK_SET); + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + + if(size == 512 || size == 0x2000) { + if(fread(eepromData, 1, size, file) != (size_t)size) { + fclose(file); + return false; + } + } else { + if(size == 0x20000) { + if(fread(flashSaveMemory, 1, 0x20000, file) != 0x20000) { + fclose(file); + return false; + } + flashSetSize(0x20000); + } else { + if(fread(flashSaveMemory, 1, 0x10000, file) != 0x10000) { + fclose(file); + return false; + } + flashSetSize(0x10000); + } + } + fclose(file); + return true; +} + +bool CPUWritePNGFile(const char *fileName) +{ + return utilWritePNGFile(fileName, 240, 160, pix); +} + +bool CPUWriteBMPFile(const char *fileName) +{ + return utilWriteBMPFile(fileName, 240, 160, pix); +} + +bool CPUIsZipFile(const char * file) +{ + if(strlen(file) > 4) { + const char * p = strrchr(file,'.'); + + if(p != NULL) { + if(_stricmp(p, ".zip") == 0) + return true; + } + } + + return false; +} + +bool CPUIsGBAImage(const char * file) +{ + cpuIsMultiBoot = false; + if(strlen(file) > 4) { + const char * p = strrchr(file,'.'); + + if(p != NULL) { + if(_stricmp(p, ".gba") == 0) + return true; + if(_stricmp(p, ".agb") == 0) + return true; + if(_stricmp(p, ".bin") == 0) + return true; + if(_stricmp(p, ".elf") == 0) + return true; + if(_stricmp(p, ".mb") == 0) { + cpuIsMultiBoot = true; + return true; + } + } + } + + return false; +} + +bool CPUIsGBABios(const char * file) +{ + if(strlen(file) > 4) { + const char * p = strrchr(file,'.'); + + if(p != NULL) { + if(_stricmp(p, ".gba") == 0) + return true; + if(_stricmp(p, ".agb") == 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; +} + +bool CPUIsELF(const char *file) +{ + if(strlen(file) > 4) { + const char * p = strrchr(file,'.'); + + if(p != NULL) { + if(_stricmp(p, ".elf") == 0) + return true; + } + } + return false; +} + +void CPUCleanUp() +{ +#ifdef PROFILING + if(profilingTicksReload) { + profCleanup(); + } +#endif + + if(rom != NULL) { + free(rom); + rom = NULL; + } + + if(vram != NULL) { + free(vram); + vram = NULL; + } + + if(paletteRAM != NULL) { + free(paletteRAM); + paletteRAM = NULL; + } + + if(internalRAM != NULL) { + free(internalRAM); + internalRAM = NULL; + } + + if(workRAM != NULL) { + free(workRAM); + workRAM = NULL; + } + + if(bios != NULL) { + free(bios); + bios = NULL; + } + + if(pix != NULL) { + free(pix); + pix = NULL; + } + + if(oam != NULL) { + free(oam); + oam = NULL; + } + + if(ioMem != NULL) { + free(ioMem); + ioMem = NULL; + } + + elfCleanUp(); + + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + + emulating = 0; +} + +int CPULoadRom(const char *szFile) +{ + romSize = 0x2000000; + if(rom != NULL) { + CPUCleanUp(); + } + + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + + rom = (u8 *)malloc(0x2000000); + if(rom == NULL) { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "ROM"); + return 0; + } + workRAM = (u8 *)calloc(1, 0x40000); + if(workRAM == NULL) { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "WRAM"); + return 0; + } + + u8 *whereToLoad = cpuIsMultiBoot ? workRAM : rom; + + if(CPUIsELF(szFile)) { + FILE *f = fopen(szFile, "rb"); + if(!f) { + systemMessage(MSG_ERROR_OPENING_IMAGE, N_("Error opening image %s"), + szFile); + free(rom); + rom = NULL; + free(workRAM); + workRAM = NULL; + return 0; + } + bool res = elfRead(szFile, romSize, f); + if(!res || romSize == 0) { + free(rom); + rom = NULL; + free(workRAM); + workRAM = NULL; + elfCleanUp(); + return 0; + } + } else if(!utilLoad(szFile, + utilIsGBAImage, + whereToLoad, + romSize)) { + free(rom); + rom = NULL; + free(workRAM); + workRAM = NULL; + return 0; + } + + u16 *temp = (u16 *)(rom+((romSize+1)&~1)); + int i; + for(i = (romSize+1)&~1; i < 0x2000000; i+=2) { + WRITE16LE(temp, (i >> 1) & 0xFFFF); + temp++; + } + + bios = (u8 *)calloc(1,0x4000); + if(bios == NULL) { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "BIOS"); + CPUCleanUp(); + return 0; + } + internalRAM = (u8 *)calloc(1,0x8000); + if(internalRAM == NULL) { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "IRAM"); + CPUCleanUp(); + return 0; + } + paletteRAM = (u8 *)calloc(1,0x400); + if(paletteRAM == NULL) { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "PRAM"); + CPUCleanUp(); + return 0; + } + vram = (u8 *)calloc(1, 0x20000); + if(vram == NULL) { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "VRAM"); + CPUCleanUp(); + return 0; + } + oam = (u8 *)calloc(1, 0x400); + if(oam == NULL) { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "OAM"); + CPUCleanUp(); + return 0; + } + pix = (u8 *)calloc(1, 4 * 241 * 162); + if(pix == NULL) { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "PIX"); + CPUCleanUp(); + return 0; + } + ioMem = (u8 *)calloc(1, 0x400); + if(ioMem == NULL) { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "IO"); + CPUCleanUp(); + return 0; + } + + flashInit(); + eepromInit(); + + CPUUpdateRenderBuffers(true); + + return romSize; +} + +void doMirroring (bool b) +{ + u32 mirroredRomSize = (((romSize)>>20) & 0x3F)<<20; + u32 mirroredRomAddress = romSize; + if ((mirroredRomSize <=0x800000) && (b)) + { + mirroredRomAddress = mirroredRomSize; + if (mirroredRomSize==0) + mirroredRomSize=0x100000; + while (mirroredRomAddress<0x01000000) + { + memcpy ((u16 *)(rom+mirroredRomAddress), (u16 *)(rom), mirroredRomSize); + mirroredRomAddress+=mirroredRomSize; + } + } +} + +void CPUUpdateRender() +{ + switch(DISPCNT & 7) { + case 0: + if((!fxOn && !windowOn && !(layerEnable & 0x8000)) || + cpuDisableSfx) + renderLine = mode0RenderLine; + else if(fxOn && !windowOn && !(layerEnable & 0x8000)) + renderLine = mode0RenderLineNoWindow; + else + renderLine = mode0RenderLineAll; + break; + case 1: + if((!fxOn && !windowOn && !(layerEnable & 0x8000)) || + cpuDisableSfx) + renderLine = mode1RenderLine; + else if(fxOn && !windowOn && !(layerEnable & 0x8000)) + renderLine = mode1RenderLineNoWindow; + else + renderLine = mode1RenderLineAll; + break; + case 2: + if((!fxOn && !windowOn && !(layerEnable & 0x8000)) || + cpuDisableSfx) + renderLine = mode2RenderLine; + else if(fxOn && !windowOn && !(layerEnable & 0x8000)) + renderLine = mode2RenderLineNoWindow; + else + renderLine = mode2RenderLineAll; + break; + case 3: + if((!fxOn && !windowOn && !(layerEnable & 0x8000)) || + cpuDisableSfx) + renderLine = mode3RenderLine; + else if(fxOn && !windowOn && !(layerEnable & 0x8000)) + renderLine = mode3RenderLineNoWindow; + else + renderLine = mode3RenderLineAll; + break; + case 4: + if((!fxOn && !windowOn && !(layerEnable & 0x8000)) || + cpuDisableSfx) + renderLine = mode4RenderLine; + else if(fxOn && !windowOn && !(layerEnable & 0x8000)) + renderLine = mode4RenderLineNoWindow; + else + renderLine = mode4RenderLineAll; + break; + case 5: + if((!fxOn && !windowOn && !(layerEnable & 0x8000)) || + cpuDisableSfx) + renderLine = mode5RenderLine; + else if(fxOn && !windowOn && !(layerEnable & 0x8000)) + renderLine = mode5RenderLineNoWindow; + else + renderLine = mode5RenderLineAll; + default: + break; + } +} + +void CPUUpdateCPSR() +{ + u32 CPSR = reg[16].I & 0x40; + if(N_FLAG) + CPSR |= 0x80000000; + if(Z_FLAG) + CPSR |= 0x40000000; + if(C_FLAG) + CPSR |= 0x20000000; + if(V_FLAG) + CPSR |= 0x10000000; + if(!armState) + CPSR |= 0x00000020; + if(!armIrqEnable) + CPSR |= 0x80; + CPSR |= (armMode & 0x1F); + reg[16].I = CPSR; +} + +void CPUUpdateFlags(bool breakLoop) +{ + u32 CPSR = reg[16].I; + + N_FLAG = (CPSR & 0x80000000) ? true: false; + Z_FLAG = (CPSR & 0x40000000) ? true: false; + C_FLAG = (CPSR & 0x20000000) ? true: false; + V_FLAG = (CPSR & 0x10000000) ? true: false; + armState = (CPSR & 0x20) ? false : true; + armIrqEnable = (CPSR & 0x80) ? false : true; + if(breakLoop) { + if (armIrqEnable && (IF & IE) && (IME & 1)) + cpuNextEvent = cpuTotalTicks; + } +} + +void CPUUpdateFlags() +{ + CPUUpdateFlags(true); +} + +#ifdef WORDS_BIGENDIAN +static void CPUSwap(volatile u32 *a, volatile u32 *b) +{ + volatile u32 c = *b; + *b = *a; + *a = c; +} +#else +static void CPUSwap(u32 *a, u32 *b) +{ + u32 c = *b; + *b = *a; + *a = c; +} +#endif + +void CPUSwitchMode(int mode, bool saveState, bool breakLoop) +{ + // if(armMode == mode) + // return; + + CPUUpdateCPSR(); + + switch(armMode) { + case 0x10: + case 0x1F: + reg[R13_USR].I = reg[13].I; + reg[R14_USR].I = reg[14].I; + reg[17].I = reg[16].I; + break; + case 0x11: + CPUSwap(®[R8_FIQ].I, ®[8].I); + CPUSwap(®[R9_FIQ].I, ®[9].I); + CPUSwap(®[R10_FIQ].I, ®[10].I); + CPUSwap(®[R11_FIQ].I, ®[11].I); + CPUSwap(®[R12_FIQ].I, ®[12].I); + reg[R13_FIQ].I = reg[13].I; + reg[R14_FIQ].I = reg[14].I; + reg[SPSR_FIQ].I = reg[17].I; + break; + case 0x12: + reg[R13_IRQ].I = reg[13].I; + reg[R14_IRQ].I = reg[14].I; + reg[SPSR_IRQ].I = reg[17].I; + break; + case 0x13: + reg[R13_SVC].I = reg[13].I; + reg[R14_SVC].I = reg[14].I; + reg[SPSR_SVC].I = reg[17].I; + break; + case 0x17: + reg[R13_ABT].I = reg[13].I; + reg[R14_ABT].I = reg[14].I; + reg[SPSR_ABT].I = reg[17].I; + break; + case 0x1b: + reg[R13_UND].I = reg[13].I; + reg[R14_UND].I = reg[14].I; + reg[SPSR_UND].I = reg[17].I; + break; + } + + u32 CPSR = reg[16].I; + u32 SPSR = reg[17].I; + + switch(mode) { + case 0x10: + case 0x1F: + reg[13].I = reg[R13_USR].I; + reg[14].I = reg[R14_USR].I; + reg[16].I = SPSR; + break; + case 0x11: + CPUSwap(®[8].I, ®[R8_FIQ].I); + CPUSwap(®[9].I, ®[R9_FIQ].I); + CPUSwap(®[10].I, ®[R10_FIQ].I); + CPUSwap(®[11].I, ®[R11_FIQ].I); + CPUSwap(®[12].I, ®[R12_FIQ].I); + reg[13].I = reg[R13_FIQ].I; + reg[14].I = reg[R14_FIQ].I; + if(saveState) + reg[17].I = CPSR; + else + reg[17].I = reg[SPSR_FIQ].I; + break; + case 0x12: + reg[13].I = reg[R13_IRQ].I; + reg[14].I = reg[R14_IRQ].I; + reg[16].I = SPSR; + if(saveState) + reg[17].I = CPSR; + else + reg[17].I = reg[SPSR_IRQ].I; + break; + case 0x13: + reg[13].I = reg[R13_SVC].I; + reg[14].I = reg[R14_SVC].I; + reg[16].I = SPSR; + if(saveState) + reg[17].I = CPSR; + else + reg[17].I = reg[SPSR_SVC].I; + break; + case 0x17: + reg[13].I = reg[R13_ABT].I; + reg[14].I = reg[R14_ABT].I; + reg[16].I = SPSR; + if(saveState) + reg[17].I = CPSR; + else + reg[17].I = reg[SPSR_ABT].I; + break; + case 0x1b: + reg[13].I = reg[R13_UND].I; + reg[14].I = reg[R14_UND].I; + reg[16].I = SPSR; + if(saveState) + reg[17].I = CPSR; + else + reg[17].I = reg[SPSR_UND].I; + break; + default: + systemMessage(MSG_UNSUPPORTED_ARM_MODE, N_("Unsupported ARM mode %02x"), mode); + break; + } + armMode = mode; + CPUUpdateFlags(breakLoop); + CPUUpdateCPSR(); +} + +void CPUSwitchMode(int mode, bool saveState) +{ + CPUSwitchMode(mode, saveState, true); +} + +void CPUUndefinedException() +{ + u32 PC = reg[15].I; + bool savedArmState = armState; + CPUSwitchMode(0x1b, true, false); + reg[14].I = PC - (savedArmState ? 4 : 2); + reg[15].I = 0x04; + armState = true; + armIrqEnable = false; + armNextPC = 0x04; + ARM_PREFETCH; + reg[15].I += 4; +} + +void CPUSoftwareInterrupt() +{ + u32 PC = reg[15].I; + bool savedArmState = armState; + CPUSwitchMode(0x13, true, false); + reg[14].I = PC - (savedArmState ? 4 : 2); + reg[15].I = 0x08; + armState = true; + armIrqEnable = false; + armNextPC = 0x08; + ARM_PREFETCH; + reg[15].I += 4; +} + +void CPUSoftwareInterrupt(int comment) +{ + static bool disableMessage = false; + if(armState) comment >>= 16; +#ifdef BKPT_SUPPORT + if(comment == 0xff) { + extern void (*dbgOutput)(const char *, u32); + dbgOutput(NULL, reg[0].I); + return; + } +#endif +#ifdef PROFILING + if(comment == 0xfe) { + profStartup(reg[0].I, reg[1].I); + return; + } + if(comment == 0xfd) { + profControl(reg[0].I); + return; + } + if(comment == 0xfc) { + profCleanup(); + return; + } + if(comment == 0xfb) { + profCount(); + return; + } +#endif + if(comment == 0xfa) { + agbPrintFlush(); + return; + } +#ifdef SDL + if(comment == 0xf9) { + emulating = 0; + cpuNextEvent = cpuTotalTicks; + cpuBreakLoop = true; + return; + } +#endif + if(useBios) { +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("SWI: %08x at %08x (0x%08x,0x%08x,0x%08x,VCOUNT = %2d)\n", comment, + armState ? armNextPC - 4: armNextPC -2, + reg[0].I, + reg[1].I, + reg[2].I, + VCOUNT); + } +#endif + CPUSoftwareInterrupt(); + return; + } + // This would be correct, but it causes problems if uncommented + // else { + // biosProtected = 0xe3a02004; + // } + + switch(comment) { + case 0x00: + BIOS_SoftReset(); + ARM_PREFETCH; + break; + case 0x01: + BIOS_RegisterRamReset(); + break; + case 0x02: +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("Halt: (VCOUNT = %2d)\n", + VCOUNT); + } +#endif + holdState = true; + holdType = -1; + cpuNextEvent = cpuTotalTicks; + break; + case 0x03: +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("Stop: (VCOUNT = %2d)\n", + VCOUNT); + } +#endif + holdState = true; + holdType = -1; + stopState = true; + cpuNextEvent = cpuTotalTicks; + break; + case 0x04: +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("IntrWait: 0x%08x,0x%08x (VCOUNT = %2d)\n", + reg[0].I, + reg[1].I, + VCOUNT); + } +#endif + CPUSoftwareInterrupt(); + break; + case 0x05: +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("VBlankIntrWait: (VCOUNT = %2d)\n", + VCOUNT); + } +#endif + CPUSoftwareInterrupt(); + break; + case 0x06: + CPUSoftwareInterrupt(); + break; + case 0x07: + CPUSoftwareInterrupt(); + break; + case 0x08: + BIOS_Sqrt(); + break; + case 0x09: + BIOS_ArcTan(); + break; + case 0x0A: + BIOS_ArcTan2(); + break; + case 0x0B: + { + int len = (reg[2].I & 0x1FFFFF) >>1; + if (!(((reg[0].I & 0xe000000) == 0) || + ((reg[0].I + len) & 0xe000000) == 0)) + { + if ((reg[2].I >> 24) & 1) + { + if ((reg[2].I >> 26) & 1) + SWITicks = (7 + memoryWait32[(reg[1].I>>24) & 0xF]) * (len>>1); + else + SWITicks = (8 + memoryWait[(reg[1].I>>24) & 0xF]) * (len); + } + else + { + if ((reg[2].I >> 26) & 1) + SWITicks = (10 + memoryWait32[(reg[0].I>>24) & 0xF] + + memoryWait32[(reg[1].I>>24) & 0xF]) * (len>>1); + else + SWITicks = (11 + memoryWait[(reg[0].I>>24) & 0xF] + + memoryWait[(reg[1].I>>24) & 0xF]) * len; + } + } + } + BIOS_CpuSet(); + break; + case 0x0C: + { + int len = (reg[2].I & 0x1FFFFF) >>5; + if (!(((reg[0].I & 0xe000000) == 0) || + ((reg[0].I + len) & 0xe000000) == 0)) + { + if ((reg[2].I >> 24) & 1) + SWITicks = (6 + memoryWait32[(reg[1].I>>24) & 0xF] + + 7 * (memoryWaitSeq32[(reg[1].I>>24) & 0xF] + 1)) * len; + else + SWITicks = (9 + memoryWait32[(reg[0].I>>24) & 0xF] + + memoryWait32[(reg[1].I>>24) & 0xF] + + 7 * (memoryWaitSeq32[(reg[0].I>>24) & 0xF] + + memoryWaitSeq32[(reg[1].I>>24) & 0xF] + 2)) * len; + } + } + BIOS_CpuFastSet(); + break; + case 0x0D: + BIOS_GetBiosChecksum(); + break; + case 0x0E: + BIOS_BgAffineSet(); + break; + case 0x0F: + BIOS_ObjAffineSet(); + break; + case 0x10: + { + int len = CPUReadHalfWord(reg[2].I); + if (!(((reg[0].I & 0xe000000) == 0) || + ((reg[0].I + len) & 0xe000000) == 0)) + SWITicks = (32 + memoryWait[(reg[0].I>>24) & 0xF]) * len; + } + BIOS_BitUnPack(); + break; + case 0x11: + { + u32 len = CPUReadMemory(reg[0].I) >> 8; + if(!(((reg[0].I & 0xe000000) == 0) || + ((reg[0].I + (len & 0x1fffff)) & 0xe000000) == 0)) + SWITicks = (9 + memoryWait[(reg[1].I>>24) & 0xF]) * len; + } + BIOS_LZ77UnCompWram(); + break; + case 0x12: + { + u32 len = CPUReadMemory(reg[0].I) >> 8; + if(!(((reg[0].I & 0xe000000) == 0) || + ((reg[0].I + (len & 0x1fffff)) & 0xe000000) == 0)) + SWITicks = (19 + memoryWait[(reg[1].I>>24) & 0xF]) * len; + } + BIOS_LZ77UnCompVram(); + break; + case 0x13: + { + u32 len = CPUReadMemory(reg[0].I) >> 8; + if(!(((reg[0].I & 0xe000000) == 0) || + ((reg[0].I + (len & 0x1fffff)) & 0xe000000) == 0)) + SWITicks = (29 + (memoryWait[(reg[0].I>>24) & 0xF]<<1)) * len; + } + BIOS_HuffUnComp(); + break; + case 0x14: + { + u32 len = CPUReadMemory(reg[0].I) >> 8; + if(!(((reg[0].I & 0xe000000) == 0) || + ((reg[0].I + (len & 0x1fffff)) & 0xe000000) == 0)) + SWITicks = (11 + memoryWait[(reg[0].I>>24) & 0xF] + + memoryWait[(reg[1].I>>24) & 0xF]) * len; + } + BIOS_RLUnCompWram(); + break; + case 0x15: + { + u32 len = CPUReadMemory(reg[0].I) >> 9; + if(!(((reg[0].I & 0xe000000) == 0) || + ((reg[0].I + (len & 0x1fffff)) & 0xe000000) == 0)) + SWITicks = (34 + (memoryWait[(reg[0].I>>24) & 0xF] << 1) + + memoryWait[(reg[1].I>>24) & 0xF]) * len; + } + BIOS_RLUnCompVram(); + break; + case 0x16: + { + u32 len = CPUReadMemory(reg[0].I) >> 8; + if(!(((reg[0].I & 0xe000000) == 0) || + ((reg[0].I + (len & 0x1fffff)) & 0xe000000) == 0)) + SWITicks = (13 + memoryWait[(reg[0].I>>24) & 0xF] + + memoryWait[(reg[1].I>>24) & 0xF]) * len; + } + BIOS_Diff8bitUnFilterWram(); + break; + case 0x17: + { + u32 len = CPUReadMemory(reg[0].I) >> 9; + if(!(((reg[0].I & 0xe000000) == 0) || + ((reg[0].I + (len & 0x1fffff)) & 0xe000000) == 0)) + SWITicks = (39 + (memoryWait[(reg[0].I>>24) & 0xF]<<1) + + memoryWait[(reg[1].I>>24) & 0xF]) * len; + } + BIOS_Diff8bitUnFilterVram(); + break; + case 0x18: + { + u32 len = CPUReadMemory(reg[0].I) >> 9; + if(!(((reg[0].I & 0xe000000) == 0) || + ((reg[0].I + (len & 0x1fffff)) & 0xe000000) == 0)) + SWITicks = (13 + memoryWait[(reg[0].I>>24) & 0xF] + + memoryWait[(reg[1].I>>24) & 0xF]) * len; + } + BIOS_Diff16bitUnFilter(); + break; + case 0x19: +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("SoundBiasSet: 0x%08x (VCOUNT = %2d)\n", + reg[0].I, + VCOUNT); + } +#endif + if(reg[0].I) + systemSoundPause(); + else + systemSoundResume(); + break; + case 0x1F: + BIOS_MidiKey2Freq(); + break; + case 0x2A: + BIOS_SndDriverJmpTableCopy(); + // let it go, because we don't really emulate this function + default: +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("SWI: %08x at %08x (0x%08x,0x%08x,0x%08x,VCOUNT = %2d)\n", comment, + armState ? armNextPC - 4: armNextPC -2, + reg[0].I, + reg[1].I, + reg[2].I, + VCOUNT); + } +#endif + + if(!disableMessage) { + systemMessage(MSG_UNSUPPORTED_BIOS_FUNCTION, + N_("Unsupported BIOS function %02x called from %08x. A BIOS file is needed in order to get correct behaviour."), + comment, + armMode ? armNextPC - 4: armNextPC - 2); + disableMessage = true; + } + break; + } +} + +void CPUCompareVCOUNT() +{ + if(VCOUNT == (DISPSTAT >> 8)) { + DISPSTAT |= 4; + UPDATE_REG(0x04, DISPSTAT); + + if(DISPSTAT & 0x20) { + IF |= 4; + UPDATE_REG(0x202, IF); + } + } else { + DISPSTAT &= 0xFFFB; + UPDATE_REG(0x4, DISPSTAT); + } + if (layerEnableDelay>0) + { + layerEnableDelay--; + if (layerEnableDelay==1) + layerEnable = layerSettings & DISPCNT; + } + +} + +void doDMA(u32 &s, u32 &d, u32 si, u32 di, u32 c, int transfer32) +{ + int sm = s >> 24; + int dm = d >> 24; + int sw = 0; + int dw = 0; + int sc = c; + + cpuDmaCount = c; + // This is done to get the correct waitstates. + if (sm>15) + sm=15; + if (dm>15) + dm=15; + + //if ((sm>=0x05) && (sm<=0x07) || (dm>=0x05) && (dm <=0x07)) + // blank = (((DISPSTAT | ((DISPSTAT>>1)&1))==1) ? true : false); + + if(transfer32) { + s &= 0xFFFFFFFC; + if(s < 0x02000000 && (reg[15].I >> 24)) { + while(c != 0) { + CPUWriteMemory(d, 0); + d += di; + c--; + } + } else { + while(c != 0) { + cpuDmaLast = CPUReadMemory(s); + CPUWriteMemory(d, cpuDmaLast); + d += di; + s += si; + c--; + } + } + } else { + s &= 0xFFFFFFFE; + si = (int)si >> 1; + di = (int)di >> 1; + if(s < 0x02000000 && (reg[15].I >> 24)) { + while(c != 0) { + CPUWriteHalfWord(d, 0); + d += di; + c--; + } + } else { + while(c != 0) { + cpuDmaLast = CPUReadHalfWord(s); + CPUWriteHalfWord(d, cpuDmaLast); + cpuDmaLast |= (cpuDmaLast<<16); + d += di; + s += si; + c--; + } + } + } + + cpuDmaCount = 0; + + int totalTicks = 0; + + if(transfer32) { + sw =1+memoryWaitSeq32[sm & 15]; + dw =1+memoryWaitSeq32[dm & 15]; + totalTicks = (sw+dw)*(sc-1) + 6 + memoryWait32[sm & 15] + + memoryWaitSeq32[dm & 15]; + } + else + { + sw = 1+memoryWaitSeq[sm & 15]; + dw = 1+memoryWaitSeq[dm & 15]; + totalTicks = (sw+dw)*(sc-1) + 6 + memoryWait[sm & 15] + + memoryWaitSeq[dm & 15]; + } + + cpuDmaTicksToUpdate += totalTicks; + +} + +void CPUCheckDMA(int reason, int dmamask) +{ + // DMA 0 + if((DM0CNT_H & 0x8000) && (dmamask & 1)) { + if(((DM0CNT_H >> 12) & 3) == reason) { + u32 sourceIncrement = 4; + u32 destIncrement = 4; + switch((DM0CNT_H >> 7) & 3) { + case 0: + break; + case 1: + sourceIncrement = (u32)-4; + break; + case 2: + sourceIncrement = 0; + break; + } + switch((DM0CNT_H >> 5) & 3) { + case 0: + break; + case 1: + destIncrement = (u32)-4; + break; + case 2: + destIncrement = 0; + break; + } +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_DMA0) { + int count = (DM0CNT_L ? DM0CNT_L : 0x4000) << 1; + if(DM0CNT_H & 0x0400) + count <<= 1; + log("DMA0: s=%08x d=%08x c=%04x count=%08x\n", dma0Source, dma0Dest, + DM0CNT_H, + count); + } +#endif + doDMA(dma0Source, dma0Dest, sourceIncrement, destIncrement, + DM0CNT_L ? DM0CNT_L : 0x4000, + DM0CNT_H & 0x0400); + cpuDmaHack = true; + + if(DM0CNT_H & 0x4000) { + IF |= 0x0100; + UPDATE_REG(0x202, IF); + cpuNextEvent = cpuTotalTicks; + } + + if(((DM0CNT_H >> 5) & 3) == 3) { + dma0Dest = DM0DAD_L | (DM0DAD_H << 16); + } + + if(!(DM0CNT_H & 0x0200) || (reason == 0)) { + DM0CNT_H &= 0x7FFF; + UPDATE_REG(0xBA, DM0CNT_H); + } + } + } + + // DMA 1 + if((DM1CNT_H & 0x8000) && (dmamask & 2)) { + if(((DM1CNT_H >> 12) & 3) == reason) { + u32 sourceIncrement = 4; + u32 destIncrement = 4; + switch((DM1CNT_H >> 7) & 3) { + case 0: + break; + case 1: + sourceIncrement = (u32)-4; + break; + case 2: + sourceIncrement = 0; + break; + } + switch((DM1CNT_H >> 5) & 3) { + case 0: + break; + case 1: + destIncrement = (u32)-4; + break; + case 2: + destIncrement = 0; + break; + } + if(reason == 3) { +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_DMA1) { + log("DMA1: s=%08x d=%08x c=%04x count=%08x\n", dma1Source, dma1Dest, + DM1CNT_H, + 16); + } +#endif + doDMA(dma1Source, dma1Dest, sourceIncrement, 0, 4, + 0x0400); + } else { +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_DMA1) { + int count = (DM1CNT_L ? DM1CNT_L : 0x4000) << 1; + if(DM1CNT_H & 0x0400) + count <<= 1; + log("DMA1: s=%08x d=%08x c=%04x count=%08x\n", dma1Source, dma1Dest, + DM1CNT_H, + count); + } +#endif + doDMA(dma1Source, dma1Dest, sourceIncrement, destIncrement, + DM1CNT_L ? DM1CNT_L : 0x4000, + DM1CNT_H & 0x0400); + } + cpuDmaHack = true; + + if(DM1CNT_H & 0x4000) { + IF |= 0x0200; + UPDATE_REG(0x202, IF); + cpuNextEvent = cpuTotalTicks; + } + + if(((DM1CNT_H >> 5) & 3) == 3) { + dma1Dest = DM1DAD_L | (DM1DAD_H << 16); + } + + if(!(DM1CNT_H & 0x0200) || (reason == 0)) { + DM1CNT_H &= 0x7FFF; + UPDATE_REG(0xC6, DM1CNT_H); + } + } + } + + // DMA 2 + if((DM2CNT_H & 0x8000) && (dmamask & 4)) { + if(((DM2CNT_H >> 12) & 3) == reason) { + u32 sourceIncrement = 4; + u32 destIncrement = 4; + switch((DM2CNT_H >> 7) & 3) { + case 0: + break; + case 1: + sourceIncrement = (u32)-4; + break; + case 2: + sourceIncrement = 0; + break; + } + switch((DM2CNT_H >> 5) & 3) { + case 0: + break; + case 1: + destIncrement = (u32)-4; + break; + case 2: + destIncrement = 0; + break; + } + if(reason == 3) { +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_DMA2) { + int count = (4) << 2; + log("DMA2: s=%08x d=%08x c=%04x count=%08x\n", dma2Source, dma2Dest, + DM2CNT_H, + count); + } +#endif + doDMA(dma2Source, dma2Dest, sourceIncrement, 0, 4, + 0x0400); + } else { +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_DMA2) { + int count = (DM2CNT_L ? DM2CNT_L : 0x4000) << 1; + if(DM2CNT_H & 0x0400) + count <<= 1; + log("DMA2: s=%08x d=%08x c=%04x count=%08x\n", dma2Source, dma2Dest, + DM2CNT_H, + count); + } +#endif + doDMA(dma2Source, dma2Dest, sourceIncrement, destIncrement, + DM2CNT_L ? DM2CNT_L : 0x4000, + DM2CNT_H & 0x0400); + } + cpuDmaHack = true; + + if(DM2CNT_H & 0x4000) { + IF |= 0x0400; + UPDATE_REG(0x202, IF); + cpuNextEvent = cpuTotalTicks; + } + + if(((DM2CNT_H >> 5) & 3) == 3) { + dma2Dest = DM2DAD_L | (DM2DAD_H << 16); + } + + if(!(DM2CNT_H & 0x0200) || (reason == 0)) { + DM2CNT_H &= 0x7FFF; + UPDATE_REG(0xD2, DM2CNT_H); + } + } + } + + // DMA 3 + if((DM3CNT_H & 0x8000) && (dmamask & 8)) { + if(((DM3CNT_H >> 12) & 3) == reason) { + u32 sourceIncrement = 4; + u32 destIncrement = 4; + switch((DM3CNT_H >> 7) & 3) { + case 0: + break; + case 1: + sourceIncrement = (u32)-4; + break; + case 2: + sourceIncrement = 0; + break; + } + switch((DM3CNT_H >> 5) & 3) { + case 0: + break; + case 1: + destIncrement = (u32)-4; + break; + case 2: + destIncrement = 0; + break; + } +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_DMA3) { + int count = (DM3CNT_L ? DM3CNT_L : 0x10000) << 1; + if(DM3CNT_H & 0x0400) + count <<= 1; + log("DMA3: s=%08x d=%08x c=%04x count=%08x\n", dma3Source, dma3Dest, + DM3CNT_H, + count); + } +#endif + doDMA(dma3Source, dma3Dest, sourceIncrement, destIncrement, + DM3CNT_L ? DM3CNT_L : 0x10000, + DM3CNT_H & 0x0400); + if(DM3CNT_H & 0x4000) { + IF |= 0x0800; + UPDATE_REG(0x202, IF); + cpuNextEvent = cpuTotalTicks; + } + + if(((DM3CNT_H >> 5) & 3) == 3) { + dma3Dest = DM3DAD_L | (DM3DAD_H << 16); + } + + if(!(DM3CNT_H & 0x0200) || (reason == 0)) { + DM3CNT_H &= 0x7FFF; + UPDATE_REG(0xDE, DM3CNT_H); + } + } + } +} + +void CPUUpdateRegister(u32 address, u16 value) +{ + switch(address) { + case 0x00: + { + if ((value & 7) >5) + DISPCNT = (value &7); + bool change = ((DISPCNT ^ value) & 0x80) ? true : false; + bool changeBG = ((DISPCNT ^ value) & 0x0F00) ? true : false; + u16 changeBGon = (((~DISPCNT) & value) & 0x0F00); + DISPCNT = (value & 0xFFF7); + UPDATE_REG(0x00, DISPCNT); + + if (changeBGon) + { + layerEnableDelay=4; + layerEnable = layerSettings & value & (~changeBGon); + } + else + layerEnable = layerSettings & value; + // CPUUpdateTicks(); + + windowOn = (layerEnable & 0x6000) ? true : false; + if(change && !((value & 0x80))) { + if(!(DISPSTAT & 1)) { + lcdTicks = 1008; + // VCOUNT = 0; + // UPDATE_REG(0x06, VCOUNT); + DISPSTAT &= 0xFFFC; + UPDATE_REG(0x04, DISPSTAT); + CPUCompareVCOUNT(); + } + // (*renderLine)(); + } + CPUUpdateRender(); + // we only care about changes in BG0-BG3 + if(changeBG) + CPUUpdateRenderBuffers(false); + } + break; + case 0x04: + DISPSTAT = (value & 0xFF38) | (DISPSTAT & 7); + UPDATE_REG(0x04, DISPSTAT); + break; + case 0x06: + // not writable + break; + case 0x08: + BG0CNT = (value & 0xDFCF); + UPDATE_REG(0x08, BG0CNT); + break; + case 0x0A: + BG1CNT = (value & 0xDFCF); + UPDATE_REG(0x0A, BG1CNT); + break; + case 0x0C: + BG2CNT = (value & 0xFFCF); + UPDATE_REG(0x0C, BG2CNT); + break; + case 0x0E: + BG3CNT = (value & 0xFFCF); + UPDATE_REG(0x0E, BG3CNT); + break; + case 0x10: + BG0HOFS = value & 511; + UPDATE_REG(0x10, BG0HOFS); + break; + case 0x12: + BG0VOFS = value & 511; + UPDATE_REG(0x12, BG0VOFS); + break; + case 0x14: + BG1HOFS = value & 511; + UPDATE_REG(0x14, BG1HOFS); + break; + case 0x16: + BG1VOFS = value & 511; + UPDATE_REG(0x16, BG1VOFS); + break; + case 0x18: + BG2HOFS = value & 511; + UPDATE_REG(0x18, BG2HOFS); + break; + case 0x1A: + BG2VOFS = value & 511; + UPDATE_REG(0x1A, BG2VOFS); + break; + case 0x1C: + BG3HOFS = value & 511; + UPDATE_REG(0x1C, BG3HOFS); + break; + case 0x1E: + BG3VOFS = value & 511; + UPDATE_REG(0x1E, BG3VOFS); + break; + case 0x20: + BG2PA = value; + UPDATE_REG(0x20, BG2PA); + break; + case 0x22: + BG2PB = value; + UPDATE_REG(0x22, BG2PB); + break; + case 0x24: + BG2PC = value; + UPDATE_REG(0x24, BG2PC); + break; + case 0x26: + BG2PD = value; + UPDATE_REG(0x26, BG2PD); + break; + case 0x28: + BG2X_L = value; + UPDATE_REG(0x28, BG2X_L); + gfxBG2Changed |= 1; + break; + case 0x2A: + BG2X_H = (value & 0xFFF); + UPDATE_REG(0x2A, BG2X_H); + gfxBG2Changed |= 1; + break; + case 0x2C: + BG2Y_L = value; + UPDATE_REG(0x2C, BG2Y_L); + gfxBG2Changed |= 2; + break; + case 0x2E: + BG2Y_H = value & 0xFFF; + UPDATE_REG(0x2E, BG2Y_H); + gfxBG2Changed |= 2; + break; + case 0x30: + BG3PA = value; + UPDATE_REG(0x30, BG3PA); + break; + case 0x32: + BG3PB = value; + UPDATE_REG(0x32, BG3PB); + break; + case 0x34: + BG3PC = value; + UPDATE_REG(0x34, BG3PC); + break; + case 0x36: + BG3PD = value; + UPDATE_REG(0x36, BG3PD); + break; + case 0x38: + BG3X_L = value; + UPDATE_REG(0x38, BG3X_L); + gfxBG3Changed |= 1; + break; + case 0x3A: + BG3X_H = value & 0xFFF; + UPDATE_REG(0x3A, BG3X_H); + gfxBG3Changed |= 1; + break; + case 0x3C: + BG3Y_L = value; + UPDATE_REG(0x3C, BG3Y_L); + gfxBG3Changed |= 2; + break; + case 0x3E: + BG3Y_H = value & 0xFFF; + UPDATE_REG(0x3E, BG3Y_H); + gfxBG3Changed |= 2; + break; + case 0x40: + WIN0H = value; + UPDATE_REG(0x40, WIN0H); + CPUUpdateWindow0(); + break; + case 0x42: + WIN1H = value; + UPDATE_REG(0x42, WIN1H); + CPUUpdateWindow1(); + break; + case 0x44: + WIN0V = value; + UPDATE_REG(0x44, WIN0V); + break; + case 0x46: + WIN1V = value; + UPDATE_REG(0x46, WIN1V); + break; + case 0x48: + WININ = value & 0x3F3F; + UPDATE_REG(0x48, WININ); + break; + case 0x4A: + WINOUT = value & 0x3F3F; + UPDATE_REG(0x4A, WINOUT); + break; + case 0x4C: + MOSAIC = value; + UPDATE_REG(0x4C, MOSAIC); + break; + case 0x50: + BLDMOD = value & 0x3FFF; + UPDATE_REG(0x50, BLDMOD); + fxOn = ((BLDMOD>>6)&3) != 0; + CPUUpdateRender(); + break; + case 0x52: + COLEV = value & 0x1F1F; + UPDATE_REG(0x52, COLEV); + break; + case 0x54: + COLY = value & 0x1F; + UPDATE_REG(0x54, COLY); + break; + case 0x60: + case 0x62: + case 0x64: + case 0x68: + case 0x6c: + case 0x70: + case 0x72: + case 0x74: + case 0x78: + case 0x7c: + case 0x80: + case 0x84: + soundEvent(address&0xFF, (u8)(value & 0xFF)); + soundEvent((address&0xFF)+1, (u8)(value>>8)); + break; + case 0x82: + case 0x88: + case 0xa0: + case 0xa2: + case 0xa4: + case 0xa6: + case 0x90: + case 0x92: + case 0x94: + case 0x96: + case 0x98: + case 0x9a: + case 0x9c: + case 0x9e: + soundEvent(address&0xFF, value); + break; + case 0xB0: + DM0SAD_L = value; + UPDATE_REG(0xB0, DM0SAD_L); + break; + case 0xB2: + DM0SAD_H = value & 0x07FF; + UPDATE_REG(0xB2, DM0SAD_H); + break; + case 0xB4: + DM0DAD_L = value; + UPDATE_REG(0xB4, DM0DAD_L); + break; + case 0xB6: + DM0DAD_H = value & 0x07FF; + UPDATE_REG(0xB6, DM0DAD_H); + break; + case 0xB8: + DM0CNT_L = value & 0x3FFF; + UPDATE_REG(0xB8, 0); + break; + case 0xBA: + { + bool start = ((DM0CNT_H ^ value) & 0x8000) ? true : false; + value &= 0xF7E0; + + DM0CNT_H = value; + UPDATE_REG(0xBA, DM0CNT_H); + + if(start && (value & 0x8000)) { + dma0Source = DM0SAD_L | (DM0SAD_H << 16); + dma0Dest = DM0DAD_L | (DM0DAD_H << 16); + CPUCheckDMA(0, 1); + } + } + break; + case 0xBC: + DM1SAD_L = value; + UPDATE_REG(0xBC, DM1SAD_L); + break; + case 0xBE: + DM1SAD_H = value & 0x0FFF; + UPDATE_REG(0xBE, DM1SAD_H); + break; + case 0xC0: + DM1DAD_L = value; + UPDATE_REG(0xC0, DM1DAD_L); + break; + case 0xC2: + DM1DAD_H = value & 0x07FF; + UPDATE_REG(0xC2, DM1DAD_H); + break; + case 0xC4: + DM1CNT_L = value & 0x3FFF; + UPDATE_REG(0xC4, 0); + break; + case 0xC6: + { + bool start = ((DM1CNT_H ^ value) & 0x8000) ? true : false; + value &= 0xF7E0; + + DM1CNT_H = value; + UPDATE_REG(0xC6, DM1CNT_H); + + if(start && (value & 0x8000)) { + dma1Source = DM1SAD_L | (DM1SAD_H << 16); + dma1Dest = DM1DAD_L | (DM1DAD_H << 16); + CPUCheckDMA(0, 2); + } + } + break; + case 0xC8: + DM2SAD_L = value; + UPDATE_REG(0xC8, DM2SAD_L); + break; + case 0xCA: + DM2SAD_H = value & 0x0FFF; + UPDATE_REG(0xCA, DM2SAD_H); + break; + case 0xCC: + DM2DAD_L = value; + UPDATE_REG(0xCC, DM2DAD_L); + break; + case 0xCE: + DM2DAD_H = value & 0x07FF; + UPDATE_REG(0xCE, DM2DAD_H); + break; + case 0xD0: + DM2CNT_L = value & 0x3FFF; + UPDATE_REG(0xD0, 0); + break; + case 0xD2: + { + bool start = ((DM2CNT_H ^ value) & 0x8000) ? true : false; + + value &= 0xF7E0; + + DM2CNT_H = value; + UPDATE_REG(0xD2, DM2CNT_H); + + if(start && (value & 0x8000)) { + dma2Source = DM2SAD_L | (DM2SAD_H << 16); + dma2Dest = DM2DAD_L | (DM2DAD_H << 16); + + CPUCheckDMA(0, 4); + } + } + break; + case 0xD4: + DM3SAD_L = value; + UPDATE_REG(0xD4, DM3SAD_L); + break; + case 0xD6: + DM3SAD_H = value & 0x0FFF; + UPDATE_REG(0xD6, DM3SAD_H); + break; + case 0xD8: + DM3DAD_L = value; + UPDATE_REG(0xD8, DM3DAD_L); + break; + case 0xDA: + DM3DAD_H = value & 0x0FFF; + UPDATE_REG(0xDA, DM3DAD_H); + break; + case 0xDC: + DM3CNT_L = value; + UPDATE_REG(0xDC, 0); + break; + case 0xDE: + { + bool start = ((DM3CNT_H ^ value) & 0x8000) ? true : false; + + value &= 0xFFE0; + + DM3CNT_H = value; + UPDATE_REG(0xDE, DM3CNT_H); + + if(start && (value & 0x8000)) { + dma3Source = DM3SAD_L | (DM3SAD_H << 16); + dma3Dest = DM3DAD_L | (DM3DAD_H << 16); + CPUCheckDMA(0,8); + } + } + break; + case 0x100: + timer0Reload = value; + interp_rate(); + break; + case 0x102: + timer0Value = value; + timerOnOffDelay|=1; + cpuNextEvent = cpuTotalTicks; + break; + case 0x104: + timer1Reload = value; + interp_rate(); + break; + case 0x106: + timer1Value = value; + timerOnOffDelay|=2; + cpuNextEvent = cpuTotalTicks; + break; + case 0x108: + timer2Reload = value; + break; + case 0x10A: + timer2Value = value; + timerOnOffDelay|=4; + cpuNextEvent = cpuTotalTicks; + break; + case 0x10C: + timer3Reload = value; + break; + case 0x10E: + timer3Value = value; + timerOnOffDelay|=8; + cpuNextEvent = cpuTotalTicks; + break; + case 0x128: + #ifdef LINK_EMULATION + if (linkenable) + { + StartLink(value); + } + else +#endif + { + if(value & 0x80) { + value &= 0xff7f; + if(value & 1 && (value & 0x4000)) { + UPDATE_REG(0x12a, 0xFF); + IF |= 0x80; + UPDATE_REG(0x202, IF); + value &= 0x7f7f; + } + } + UPDATE_REG(0x128, value); + } + break; + case 0x12a: + #ifdef LINK_EMULATION + if(linkenable && lspeed) + LinkSSend(value); + #endif + { + UPDATE_REG(0x134, value); + } + break; + case 0x130: + P1 |= (value & 0x3FF); + UPDATE_REG(0x130, P1); + break; + case 0x132: + UPDATE_REG(0x132, value & 0xC3FF); + break; + case 0x134: +#ifdef LINK_EMULATION + if (linkenable) + StartGPLink(value); + else +#endif + UPDATE_REG(0x134, value); + + break; + case 0x140: +#ifdef LINK_EMULATION + if (linkenable) + StartJOYLink(value); + else +#endif + UPDATE_REG(0x140, value); + + break; + case 0x200: + IE = value & 0x3FFF; + UPDATE_REG(0x200, IE); + if ((IME & 1) && (IF & IE) && armIrqEnable) + cpuNextEvent = cpuTotalTicks; + break; + case 0x202: + IF ^= (value & IF); + UPDATE_REG(0x202, IF); + break; + case 0x204: + { + memoryWait[0x0e] = memoryWaitSeq[0x0e] = gamepakRamWaitState[value & 3]; + + if(!speedHack) { + memoryWait[0x08] = memoryWait[0x09] = gamepakWaitState[(value >> 2) & 3]; + memoryWaitSeq[0x08] = memoryWaitSeq[0x09] = + gamepakWaitState0[(value >> 4) & 1]; + + memoryWait[0x0a] = memoryWait[0x0b] = gamepakWaitState[(value >> 5) & 3]; + memoryWaitSeq[0x0a] = memoryWaitSeq[0x0b] = + gamepakWaitState1[(value >> 7) & 1]; + + memoryWait[0x0c] = memoryWait[0x0d] = gamepakWaitState[(value >> 8) & 3]; + memoryWaitSeq[0x0c] = memoryWaitSeq[0x0d] = + gamepakWaitState2[(value >> 10) & 1]; + } else { + memoryWait[0x08] = memoryWait[0x09] = 3; + memoryWaitSeq[0x08] = memoryWaitSeq[0x09] = 1; + + memoryWait[0x0a] = memoryWait[0x0b] = 3; + memoryWaitSeq[0x0a] = memoryWaitSeq[0x0b] = 1; + + memoryWait[0x0c] = memoryWait[0x0d] = 3; + memoryWaitSeq[0x0c] = memoryWaitSeq[0x0d] = 1; + } + + for(int i = 8; i < 15; i++) { + memoryWait32[i] = memoryWait[i] + memoryWaitSeq[i] + 1; + memoryWaitSeq32[i] = memoryWaitSeq[i]*2 + 1; + } + + if((value & 0x4000) == 0x4000) { + busPrefetchEnable = true; + busPrefetch = false; + busPrefetchCount = 0; + } else { + busPrefetchEnable = false; + busPrefetch = false; + busPrefetchCount = 0; + } + UPDATE_REG(0x204, value & 0x7FFF); + + } + break; + case 0x208: + IME = value & 1; + UPDATE_REG(0x208, IME); + if ((IME & 1) && (IF & IE) && armIrqEnable) + cpuNextEvent = cpuTotalTicks; + break; + case 0x300: + if(value != 0) + value &= 0xFFFE; + UPDATE_REG(0x300, value); + break; + default: + UPDATE_REG(address&0x3FE, value); + break; + } +} + +void applyTimer () +{ + if (timerOnOffDelay & 1) + { + timer0ClockReload = TIMER_TICKS[timer0Value & 3]; + if(!timer0On && (timer0Value & 0x80)) { + // reload the counter + TM0D = timer0Reload; + timer0Ticks = (0x10000 - TM0D) << timer0ClockReload; + UPDATE_REG(0x100, TM0D); + } + timer0On = timer0Value & 0x80 ? true : false; + TM0CNT = timer0Value & 0xC7; + interp_rate(); + UPDATE_REG(0x102, TM0CNT); + // CPUUpdateTicks(); + } + if (timerOnOffDelay & 2) + { + timer1ClockReload = TIMER_TICKS[timer1Value & 3]; + if(!timer1On && (timer1Value & 0x80)) { + // reload the counter + TM1D = timer1Reload; + timer1Ticks = (0x10000 - TM1D) << timer1ClockReload; + UPDATE_REG(0x104, TM1D); + } + timer1On = timer1Value & 0x80 ? true : false; + TM1CNT = timer1Value & 0xC7; + interp_rate(); + UPDATE_REG(0x106, TM1CNT); + } + if (timerOnOffDelay & 4) + { + timer2ClockReload = TIMER_TICKS[timer2Value & 3]; + if(!timer2On && (timer2Value & 0x80)) { + // reload the counter + TM2D = timer2Reload; + timer2Ticks = (0x10000 - TM2D) << timer2ClockReload; + UPDATE_REG(0x108, TM2D); + } + timer2On = timer2Value & 0x80 ? true : false; + TM2CNT = timer2Value & 0xC7; + UPDATE_REG(0x10A, TM2CNT); + } + if (timerOnOffDelay & 8) + { + timer3ClockReload = TIMER_TICKS[timer3Value & 3]; + if(!timer3On && (timer3Value & 0x80)) { + // reload the counter + TM3D = timer3Reload; + timer3Ticks = (0x10000 - TM3D) << timer3ClockReload; + UPDATE_REG(0x10C, TM3D); + } + timer3On = timer3Value & 0x80 ? true : false; + TM3CNT = timer3Value & 0xC7; + UPDATE_REG(0x10E, TM3CNT); + } + cpuNextEvent = CPUUpdateTicks(); + timerOnOffDelay = 0; +} + +u8 cpuBitsSet[256]; +u8 cpuLowestBitSet[256]; + +void CPUInit(const char *biosFileName, bool useBiosFile) +{ +#ifdef WORDS_BIGENDIAN + if(!cpuBiosSwapped) { + for(unsigned int i = 0; i < sizeof(myROM)/4; i++) { + WRITE32LE(&myROM[i], myROM[i]); + } + cpuBiosSwapped = true; + } +#endif + gbaSaveType = 0; + eepromInUse = 0; + saveType = 0; + useBios = false; + + if(useBiosFile) { + int size = 0x4000; + if(utilLoad(biosFileName, + CPUIsGBABios, + bios, + size)) { + if(size == 0x4000) + useBios = true; + else + systemMessage(MSG_INVALID_BIOS_FILE_SIZE, N_("Invalid BIOS file size")); + } + } + + if(!useBios) { + memcpy(bios, myROM, sizeof(myROM)); + } + + int i = 0; + + biosProtected[0] = 0x00; + biosProtected[1] = 0xf0; + biosProtected[2] = 0x29; + biosProtected[3] = 0xe1; + + for(i = 0; i < 256; i++) { + int count = 0; + int j; + for(j = 0; j < 8; j++) + if(i & (1 << j)) + count++; + cpuBitsSet[i] = count; + + for(j = 0; j < 8; j++) + if(i & (1 << j)) + break; + cpuLowestBitSet[i] = j; + } + + for(i = 0; i < 0x400; i++) + ioReadable[i] = true; + for(i = 0x10; i < 0x48; i++) + ioReadable[i] = false; + for(i = 0x4c; i < 0x50; i++) + ioReadable[i] = false; + for(i = 0x54; i < 0x60; i++) + ioReadable[i] = false; + for(i = 0x8c; i < 0x90; i++) + ioReadable[i] = false; + for(i = 0xa0; i < 0xb8; i++) + ioReadable[i] = false; + for(i = 0xbc; i < 0xc4; i++) + ioReadable[i] = false; + for(i = 0xc8; i < 0xd0; i++) + ioReadable[i] = false; + for(i = 0xd4; i < 0xdc; i++) + ioReadable[i] = false; + for(i = 0xe0; i < 0x100; i++) + ioReadable[i] = false; + for(i = 0x110; i < 0x120; i++) + ioReadable[i] = false; + for(i = 0x12c; i < 0x130; i++) + ioReadable[i] = false; + for(i = 0x138; i < 0x140; i++) + ioReadable[i] = false; + for(i = 0x144; i < 0x150; i++) + ioReadable[i] = false; + for(i = 0x15c; i < 0x200; i++) + ioReadable[i] = false; + for(i = 0x20c; i < 0x300; i++) + ioReadable[i] = false; + for(i = 0x304; i < 0x400; i++) + ioReadable[i] = false; + + if(romSize < 0x1fe2000) { + *((u16 *)&rom[0x1fe209c]) = 0xdffa; // SWI 0xFA + *((u16 *)&rom[0x1fe209e]) = 0x4770; // BX LR + } else { + agbPrintEnable(false); + } +} + +void CPUReset() +{ + if(gbaSaveType == 0) { + if(eepromInUse) + gbaSaveType = 3; + else + switch(saveType) { + case 1: + gbaSaveType = 1; + break; + case 2: + gbaSaveType = 2; + break; + } + } + rtcReset(); + // clean registers + memset(®[0], 0, sizeof(reg)); + // clean OAM + memset(oam, 0, 0x400); + // clean palette + memset(paletteRAM, 0, 0x400); + // clean picture + memset(pix, 0, 4*160*240); + // clean vram + memset(vram, 0, 0x20000); + // clean io memory + memset(ioMem, 0, 0x400); + + DISPCNT = 0x0080; + DISPSTAT = 0x0000; + VCOUNT = (useBios && !skipBios) ? 0 :0x007E; + BG0CNT = 0x0000; + BG1CNT = 0x0000; + BG2CNT = 0x0000; + BG3CNT = 0x0000; + BG0HOFS = 0x0000; + BG0VOFS = 0x0000; + BG1HOFS = 0x0000; + BG1VOFS = 0x0000; + BG2HOFS = 0x0000; + BG2VOFS = 0x0000; + BG3HOFS = 0x0000; + BG3VOFS = 0x0000; + BG2PA = 0x0100; + BG2PB = 0x0000; + BG2PC = 0x0000; + BG2PD = 0x0100; + BG2X_L = 0x0000; + BG2X_H = 0x0000; + BG2Y_L = 0x0000; + BG2Y_H = 0x0000; + BG3PA = 0x0100; + BG3PB = 0x0000; + BG3PC = 0x0000; + BG3PD = 0x0100; + BG3X_L = 0x0000; + BG3X_H = 0x0000; + BG3Y_L = 0x0000; + BG3Y_H = 0x0000; + WIN0H = 0x0000; + WIN1H = 0x0000; + WIN0V = 0x0000; + WIN1V = 0x0000; + WININ = 0x0000; + WINOUT = 0x0000; + MOSAIC = 0x0000; + BLDMOD = 0x0000; + COLEV = 0x0000; + COLY = 0x0000; + DM0SAD_L = 0x0000; + DM0SAD_H = 0x0000; + DM0DAD_L = 0x0000; + DM0DAD_H = 0x0000; + DM0CNT_L = 0x0000; + DM0CNT_H = 0x0000; + DM1SAD_L = 0x0000; + DM1SAD_H = 0x0000; + DM1DAD_L = 0x0000; + DM1DAD_H = 0x0000; + DM1CNT_L = 0x0000; + DM1CNT_H = 0x0000; + DM2SAD_L = 0x0000; + DM2SAD_H = 0x0000; + DM2DAD_L = 0x0000; + DM2DAD_H = 0x0000; + DM2CNT_L = 0x0000; + DM2CNT_H = 0x0000; + DM3SAD_L = 0x0000; + DM3SAD_H = 0x0000; + DM3DAD_L = 0x0000; + DM3DAD_H = 0x0000; + DM3CNT_L = 0x0000; + DM3CNT_H = 0x0000; + TM0D = 0x0000; + TM0CNT = 0x0000; + TM1D = 0x0000; + TM1CNT = 0x0000; + TM2D = 0x0000; + TM2CNT = 0x0000; + TM3D = 0x0000; + TM3CNT = 0x0000; + P1 = 0x03FF; + IE = 0x0000; + IF = 0x0000; + IME = 0x0000; + + armMode = 0x1F; + + if(cpuIsMultiBoot) { + reg[13].I = 0x03007F00; + reg[15].I = 0x02000000; + reg[16].I = 0x00000000; + reg[R13_IRQ].I = 0x03007FA0; + reg[R13_SVC].I = 0x03007FE0; + armIrqEnable = true; + } else { + if(useBios && !skipBios) { + reg[15].I = 0x00000000; + armMode = 0x13; + armIrqEnable = false; + } else { + reg[13].I = 0x03007F00; + reg[15].I = 0x08000000; + reg[16].I = 0x00000000; + reg[R13_IRQ].I = 0x03007FA0; + reg[R13_SVC].I = 0x03007FE0; + armIrqEnable = true; + } + } + armState = true; + C_FLAG = V_FLAG = N_FLAG = Z_FLAG = false; + UPDATE_REG(0x00, DISPCNT); + UPDATE_REG(0x06, VCOUNT); + UPDATE_REG(0x20, BG2PA); + UPDATE_REG(0x26, BG2PD); + UPDATE_REG(0x30, BG3PA); + UPDATE_REG(0x36, BG3PD); + UPDATE_REG(0x130, P1); + UPDATE_REG(0x88, 0x200); + + // disable FIQ + reg[16].I |= 0x40; + + CPUUpdateCPSR(); + + armNextPC = reg[15].I; + reg[15].I += 4; + + // reset internal state + holdState = false; + holdType = 0; + + biosProtected[0] = 0x00; + biosProtected[1] = 0xf0; + biosProtected[2] = 0x29; + biosProtected[3] = 0xe1; + + lcdTicks = (useBios && !skipBios) ? 1008 : 208; + timer0On = false; + timer0Ticks = 0; + timer0Reload = 0; + timer0ClockReload = 0; + timer1On = false; + timer1Ticks = 0; + timer1Reload = 0; + timer1ClockReload = 0; + timer2On = false; + timer2Ticks = 0; + timer2Reload = 0; + timer2ClockReload = 0; + timer3On = false; + timer3Ticks = 0; + timer3Reload = 0; + timer3ClockReload = 0; + dma0Source = 0; + dma0Dest = 0; + dma1Source = 0; + dma1Dest = 0; + dma2Source = 0; + dma2Dest = 0; + dma3Source = 0; + dma3Dest = 0; + cpuSaveGameFunc = flashSaveDecide; + renderLine = mode0RenderLine; + fxOn = false; + windowOn = false; + frameCount = 0; + saveType = 0; + layerEnable = DISPCNT & layerSettings; + + CPUUpdateRenderBuffers(true); + + for(int i = 0; i < 256; i++) { + map[i].address = (u8 *)&dummyAddress; + map[i].mask = 0; + } + + map[0].address = bios; + map[0].mask = 0x3FFF; + map[2].address = workRAM; + map[2].mask = 0x3FFFF; + map[3].address = internalRAM; + map[3].mask = 0x7FFF; + map[4].address = ioMem; + map[4].mask = 0x3FF; + map[5].address = paletteRAM; + map[5].mask = 0x3FF; + map[6].address = vram; + map[6].mask = 0x1FFFF; + map[7].address = oam; + map[7].mask = 0x3FF; + map[8].address = rom; + map[8].mask = 0x1FFFFFF; + map[9].address = rom; + map[9].mask = 0x1FFFFFF; + map[10].address = rom; + map[10].mask = 0x1FFFFFF; + map[12].address = rom; + map[12].mask = 0x1FFFFFF; + map[14].address = flashSaveMemory; + map[14].mask = 0xFFFF; + + eepromReset(); + flashReset(); + + soundReset(); + + CPUUpdateWindow0(); + CPUUpdateWindow1(); + + // make sure registers are correctly initialized if not using BIOS + if(!useBios) { + if(cpuIsMultiBoot) + BIOS_RegisterRamReset(0xfe); + else + BIOS_RegisterRamReset(0xff); + } else { + if(cpuIsMultiBoot) + BIOS_RegisterRamReset(0xfe); + } + + switch(cpuSaveType) { + case 0: // automatic + cpuSramEnabled = true; + cpuFlashEnabled = true; + cpuEEPROMEnabled = true; + cpuEEPROMSensorEnabled = false; + saveType = gbaSaveType = 0; + break; + case 1: // EEPROM + cpuSramEnabled = false; + cpuFlashEnabled = false; + cpuEEPROMEnabled = true; + cpuEEPROMSensorEnabled = false; + saveType = gbaSaveType = 3; + // EEPROM usage is automatically detected + break; + case 2: // SRAM + cpuSramEnabled = true; + cpuFlashEnabled = false; + cpuEEPROMEnabled = false; + cpuEEPROMSensorEnabled = false; + cpuSaveGameFunc = sramDelayedWrite; // to insure we detect the write + saveType = gbaSaveType = 1; + break; + case 3: // FLASH + cpuSramEnabled = false; + cpuFlashEnabled = true; + cpuEEPROMEnabled = false; + cpuEEPROMSensorEnabled = false; + cpuSaveGameFunc = flashDelayedWrite; // to insure we detect the write + saveType = gbaSaveType = 2; + break; + case 4: // EEPROM+Sensor + cpuSramEnabled = false; + cpuFlashEnabled = false; + cpuEEPROMEnabled = true; + cpuEEPROMSensorEnabled = true; + // EEPROM usage is automatically detected + saveType = gbaSaveType = 3; + break; + case 5: // NONE + cpuSramEnabled = false; + cpuFlashEnabled = false; + cpuEEPROMEnabled = false; + cpuEEPROMSensorEnabled = false; + // no save at all + saveType = gbaSaveType = 5; + break; + } + + ARM_PREFETCH; + + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + + cpuDmaHack = false; + + lastTime = systemGetClock(); + + SWITicks = 0; +} + +void CPUInterrupt() +{ + u32 PC = reg[15].I; + bool savedState = armState; + CPUSwitchMode(0x12, true, false); + reg[14].I = PC; + if(!savedState) + reg[14].I += 2; + reg[15].I = 0x18; + armState = true; + armIrqEnable = false; + + armNextPC = reg[15].I; + reg[15].I += 4; + ARM_PREFETCH; + + // if(!holdState) + biosProtected[0] = 0x02; + biosProtected[1] = 0xc0; + biosProtected[2] = 0x5e; + biosProtected[3] = 0xe5; +} + +#ifdef SDL +void log(const char *defaultMsg, ...) +{ + char buffer[2048]; + va_list valist; + + va_start(valist, defaultMsg); + vsprintf(buffer, defaultMsg, valist); + + if(out == NULL) { + out = fopen("trace.log","w"); + } + + fputs(buffer, out); + + va_end(valist); +} +#else +extern void winlog(const char *, ...); +#endif + +void CPULoop(int ticks) +{ + int clockTicks; + int timerOverflow = 0; + // variable used by the CPU core + cpuTotalTicks = 0; +#ifdef LINK_EMULATION + if(linkenable) + cpuNextEvent = 1; +#endif + cpuBreakLoop = false; + cpuNextEvent = CPUUpdateTicks(); + if(cpuNextEvent > ticks) + cpuNextEvent = ticks; + + + for(;;) { +#ifndef FINAL_VERSION + if(systemDebug) { + if(systemDebug >= 10 && !holdState) { + CPUUpdateCPSR(); +#ifdef BKPT_SUPPORT + if (debugger_last) + { + sprintf(buffer, "R00=%08x R01=%08x R02=%08x R03=%08x R04=%08x R05=%08x R06=%08x R07=%08x R08=%08x R09=%08x R10=%08x R11=%08x R12=%08x R13=%08x R14=%08x R15=%08x R16=%08x R17=%08x\n", + oldreg[0], oldreg[1], oldreg[2], oldreg[3], oldreg[4], oldreg[5], + oldreg[6], oldreg[7], oldreg[8], oldreg[9], oldreg[10], oldreg[11], + oldreg[12], oldreg[13], oldreg[14], oldreg[15], oldreg[16], + oldreg[17]); + } +#endif + sprintf(buffer, "R00=%08x R01=%08x R02=%08x R03=%08x R04=%08x R05=%08x R06=%08x R07=%08x R08=%08x R09=%08x R10=%08x R11=%08x R12=%08x R13=%08x R14=%08x R15=%08x R16=%08x R17=%08x\n", + reg[0].I, reg[1].I, reg[2].I, reg[3].I, reg[4].I, reg[5].I, + reg[6].I, reg[7].I, reg[8].I, reg[9].I, reg[10].I, reg[11].I, + reg[12].I, reg[13].I, reg[14].I, reg[15].I, reg[16].I, + reg[17].I); +#ifdef SDL + log(buffer); +#else + winlog(buffer); +#endif + } else if(!holdState) { + sprintf(buffer, "PC=%08x\n", armNextPC); +#ifdef SDL + log(buffer); +#else + winlog(buffer); +#endif + } + } +#endif /* FINAL_VERSION */ + + if(!holdState && !SWITicks) { + if(armState) { + if (!armExecute()) + return; + } else { + if (!thumbExecute()) + return; + } + clockTicks = 0; + } else + clockTicks = CPUUpdateTicks(); + + cpuTotalTicks += clockTicks; + + + if(cpuTotalTicks >= cpuNextEvent) { + int remainingTicks = cpuTotalTicks - cpuNextEvent; + + if (SWITicks) + { + SWITicks-=clockTicks; + if (SWITicks<0) + SWITicks = 0; + } + + clockTicks = cpuNextEvent; + cpuTotalTicks = 0; + cpuDmaHack = false; + + updateLoop: + + if (IRQTicks) + { + IRQTicks -= clockTicks; + if (IRQTicks<0) + IRQTicks = 0; + } + + lcdTicks -= clockTicks; + + + if(lcdTicks <= 0) { + if(DISPSTAT & 1) { // V-BLANK + // if in V-Blank mode, keep computing... + if(DISPSTAT & 2) { + lcdTicks += 1008; + VCOUNT++; + UPDATE_REG(0x06, VCOUNT); + DISPSTAT &= 0xFFFD; + UPDATE_REG(0x04, DISPSTAT); + CPUCompareVCOUNT(); + } else { + lcdTicks += 224; + DISPSTAT |= 2; + UPDATE_REG(0x04, DISPSTAT); + if(DISPSTAT & 16) { + IF |= 2; + UPDATE_REG(0x202, IF); + } + } + + if(VCOUNT >= 228) { //Reaching last line + DISPSTAT &= 0xFFFC; + UPDATE_REG(0x04, DISPSTAT); + VCOUNT = 0; + UPDATE_REG(0x06, VCOUNT); + CPUCompareVCOUNT(); + } + } else { + int framesToSkip = systemFrameSkip; + if(speedup) + framesToSkip = 9; // try 6 FPS during speedup + + if(DISPSTAT & 2) { + // if in H-Blank, leave it and move to drawing mode + VCOUNT++; + UPDATE_REG(0x06, VCOUNT); + + lcdTicks += 1008; + DISPSTAT &= 0xFFFD; + if(VCOUNT == 160) { + count++; + systemFrame(); + + if((count % 10) == 0) { + system10Frames(60); + } + if(count == 60) { + u32 time = systemGetClock(); + if(time != lastTime) { + u32 t = 100000/(time - lastTime); + systemShowSpeed(t); + } else + systemShowSpeed(0); + lastTime = time; + count = 0; + } + u32 joy = 0; + // update joystick information + if(systemReadJoypads()) + // read default joystick + joy = systemReadJoypad(-1); + P1 = 0x03FF ^ (joy & 0x3FF); + if(cpuEEPROMSensorEnabled) + systemUpdateMotionSensor(); + UPDATE_REG(0x130, P1); + u16 P1CNT = READ16LE(((u16 *)&ioMem[0x132])); + // this seems wrong, but there are cases where the game + // can enter the stop state without requesting an IRQ from + // the joypad. + if((P1CNT & 0x4000) || stopState) { + u16 p1 = (0x3FF ^ P1) & 0x3FF; + if(P1CNT & 0x8000) { + if(p1 == (P1CNT & 0x3FF)) { + IF |= 0x1000; + UPDATE_REG(0x202, IF); + } + } else { + if(p1 & P1CNT) { + IF |= 0x1000; + UPDATE_REG(0x202, IF); + } + } + } + + u32 ext = (joy >> 10); + // If no (m) code is enabled, apply the cheats at each LCDline + if((cheatsEnabled) && (mastercode==0)) + remainingTicks += cheatsCheckKeys(P1^0x3FF, ext); + speedup = (ext & 1) ? true : false; + capture = (ext & 2) ? true : false; + + if(capture && !capturePrevious) { + captureNumber++; + systemScreenCapture(captureNumber); + } + capturePrevious = capture; + + DISPSTAT |= 1; + DISPSTAT &= 0xFFFD; + UPDATE_REG(0x04, DISPSTAT); + if(DISPSTAT & 0x0008) { + IF |= 1; + UPDATE_REG(0x202, IF); + } + CPUCheckDMA(1, 0x0f); + if(frameCount >= framesToSkip) { + systemDrawScreen(); + frameCount = 0; + } else + frameCount++; + if(systemPauseOnFrame()) + ticks = 0; + } + + UPDATE_REG(0x04, DISPSTAT); + CPUCompareVCOUNT(); + + } else { + if(frameCount >= framesToSkip) + { + (*renderLine)(); + switch(systemColorDepth) { + case 16: + { + u16 *dest = (u16 *)pix + 242 * (VCOUNT+1); + for(int x = 0; x < 240;) { + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + } + // for filters that read past the screen + *dest++ = 0; + } + break; + case 24: + { + u8 *dest = (u8 *)pix + 240 * VCOUNT * 3; + for(int x = 0; x < 240;) { + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + } + } + break; + case 32: + { + u32 *dest = (u32 *)pix + 241 * (VCOUNT+1); + for(int x = 0; x < 240; ) { + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + } + } + break; + } + } + // entering H-Blank + DISPSTAT |= 2; + UPDATE_REG(0x04, DISPSTAT); + lcdTicks += 224; + CPUCheckDMA(2, 0x0f); + if(DISPSTAT & 16) { + IF |= 2; + UPDATE_REG(0x202, IF); + } + } + } + } + + // we shouldn't be doing sound in stop state, but we loose synchronization + // if sound is disabled, so in stop state, soundTick will just produce + // mute sound + soundTicks -= clockTicks; + if(soundTicks <= 0) { + psoundTickfn(); + soundTicks += SOUND_CLOCK_TICKS; + } + + if(!stopState) { + if(timer0On) { + timer0Ticks -= clockTicks; + if(timer0Ticks <= 0) { + timer0Ticks += (0x10000 - timer0Reload) << timer0ClockReload; + timerOverflow |= 1; + soundTimerOverflow(0); + if(TM0CNT & 0x40) { + IF |= 0x08; + UPDATE_REG(0x202, IF); + } + } + TM0D = 0xFFFF - (timer0Ticks >> timer0ClockReload); + UPDATE_REG(0x100, TM0D); + } + + if(timer1On) { + if(TM1CNT & 4) { + if(timerOverflow & 1) { + TM1D++; + if(TM1D == 0) { + TM1D += timer1Reload; + timerOverflow |= 2; + soundTimerOverflow(1); + if(TM1CNT & 0x40) { + IF |= 0x10; + UPDATE_REG(0x202, IF); + } + } + UPDATE_REG(0x104, TM1D); + } + } else { + timer1Ticks -= clockTicks; + if(timer1Ticks <= 0) { + timer1Ticks += (0x10000 - timer1Reload) << timer1ClockReload; + timerOverflow |= 2; + soundTimerOverflow(1); + if(TM1CNT & 0x40) { + IF |= 0x10; + UPDATE_REG(0x202, IF); + } + } + TM1D = 0xFFFF - (timer1Ticks >> timer1ClockReload); + UPDATE_REG(0x104, TM1D); + } + } + + if(timer2On) { + if(TM2CNT & 4) { + if(timerOverflow & 2) { + TM2D++; + if(TM2D == 0) { + TM2D += timer2Reload; + timerOverflow |= 4; + if(TM2CNT & 0x40) { + IF |= 0x20; + UPDATE_REG(0x202, IF); + } + } + UPDATE_REG(0x108, TM2D); + } + } else { + timer2Ticks -= clockTicks; + if(timer2Ticks <= 0) { + timer2Ticks += (0x10000 - timer2Reload) << timer2ClockReload; + timerOverflow |= 4; + if(TM2CNT & 0x40) { + IF |= 0x20; + UPDATE_REG(0x202, IF); + } + } + TM2D = 0xFFFF - (timer2Ticks >> timer2ClockReload); + UPDATE_REG(0x108, TM2D); + } + } + + if(timer3On) { + if(TM3CNT & 4) { + if(timerOverflow & 4) { + TM3D++; + if(TM3D == 0) { + TM3D += timer3Reload; + if(TM3CNT & 0x40) { + IF |= 0x40; + UPDATE_REG(0x202, IF); + } + } + UPDATE_REG(0x10C, TM3D); + } + } else { + timer3Ticks -= clockTicks; + if(timer3Ticks <= 0) { + timer3Ticks += (0x10000 - timer3Reload) << timer3ClockReload; + if(TM3CNT & 0x40) { + IF |= 0x40; + UPDATE_REG(0x202, IF); + } + } + TM3D = 0xFFFF - (timer3Ticks >> timer3ClockReload); + UPDATE_REG(0x10C, TM3D); + } + } + } + + timerOverflow = 0; + + + +#ifdef PROFILING + profilingTicks -= clockTicks; + if(profilingTicks <= 0) { + profilingTicks += profilingTicksReload; + if(profilSegment) { + profile_segment *seg = profilSegment; + do { + u16 *b = (u16 *)seg->sbuf; + int pc = ((reg[15].I - seg->s_lowpc) * seg->s_scale)/0x10000; + if(pc >= 0 && pc < seg->ssiz) { + b[pc]++; + break; + } + + seg = seg->next; + } while(seg); + } + } +#endif + + ticks -= clockTicks; +#ifdef LINK_EMULATION + if (linkenable) + LinkUpdate(clockTicks); +#endif + cpuNextEvent = CPUUpdateTicks(); + + if(cpuDmaTicksToUpdate > 0) { + if(cpuDmaTicksToUpdate > cpuNextEvent) + clockTicks = cpuNextEvent; + else + clockTicks = cpuDmaTicksToUpdate; + cpuDmaTicksToUpdate -= clockTicks; + if(cpuDmaTicksToUpdate < 0) + cpuDmaTicksToUpdate = 0; + cpuDmaHack = true; + goto updateLoop; + } +#ifdef LINK_EMULATION + if(linkenable) + cpuNextEvent = 1; +#endif + if(IF && (IME & 1) && armIrqEnable) { + int res = IF & IE; + if(stopState) + res &= 0x3080; + if(res) { + if (intState) + { + if (!IRQTicks) + { + CPUInterrupt(); + intState = false; + holdState = false; + stopState = false; + holdType = 0; + } + } + else + { + if (!holdState) + { + intState = true; + IRQTicks=7; + if (cpuNextEvent> IRQTicks) + cpuNextEvent = IRQTicks; + } + else + { + CPUInterrupt(); + holdState = false; + stopState = false; + holdType = 0; + } + } + + // Stops the SWI Ticks emulation if an IRQ is executed + //(to avoid problems with nested IRQ/SWI) + if (SWITicks) + SWITicks = 0; + } + } + + if(remainingTicks > 0) { + if(remainingTicks > cpuNextEvent) + clockTicks = cpuNextEvent; + else + clockTicks = remainingTicks; + remainingTicks -= clockTicks; + if(remainingTicks < 0) + remainingTicks = 0; + goto updateLoop; + } + + if (timerOnOffDelay) + applyTimer(); + + if(cpuNextEvent > ticks) + cpuNextEvent = ticks; + + if(ticks <= 0 || cpuBreakLoop) + break; + + } + } +} + + + +struct EmulatedSystem GBASystem = { + // emuMain + CPULoop, + // emuReset + CPUReset, + // emuCleanUp + CPUCleanUp, + // emuReadBattery + CPUReadBatteryFile, + // emuWriteBattery + CPUWriteBatteryFile, + // emuReadState + CPUReadState, + // emuWriteState + CPUWriteState, + // emuReadMemState + CPUReadMemState, + // emuWriteMemState + CPUWriteMemState, + // emuWritePNG + CPUWritePNGFile, + // emuWriteBMP + CPUWriteBMPFile, + // emuUpdateCPSR + CPUUpdateCPSR, + // emuHasDebugger + true, + // emuCount +#ifdef FINAL_VERSION + 250000 +#else + 5000 +#endif +};