pokeemerald/src/main.c

446 lines
10 KiB
C
Raw Normal View History

2017-02-04 01:44:06 +01:00
#include "global.h"
2017-11-13 07:01:27 +01:00
#include "crt0.h"
#include "malloc.h"
2017-11-09 01:29:37 +01:00
#include "link.h"
2017-11-13 07:01:27 +01:00
#include "link_rfu.h"
#include "librfu.h"
#include "m4a.h"
2017-11-13 07:01:27 +01:00
#include "bg.h"
#include "rtc.h"
2018-02-03 15:42:19 +01:00
#include "scanline_effect.h"
2017-11-13 07:01:27 +01:00
#include "overworld.h"
#include "play_time.h"
#include "random.h"
#include "dma3.h"
2017-02-04 01:44:06 +01:00
#include "gba/flash_internal.h"
2017-11-13 07:01:27 +01:00
#include "load_save.h"
#include "gpu_regs.h"
#include "agb_flash.h"
#include "sound.h"
2017-09-05 20:13:34 +02:00
#include "battle.h"
2017-11-13 07:01:27 +01:00
#include "battle_controllers.h"
#include "text.h"
#include "intro.h"
#include "main.h"
2019-01-13 13:15:23 +01:00
#include "trainer_hill.h"
2017-02-04 01:44:06 +01:00
static void VBlankIntr(void);
static void HBlankIntr(void);
static void VCountIntr(void);
static void SerialIntr(void);
static void IntrDummy(void);
const u8 gGameVersion = GAME_VERSION;
2017-02-04 01:44:06 +01:00
const u8 gGameLanguage = GAME_LANGUAGE; // English
const char BuildDateTime[] = "2005 02 21 11:10";
const IntrFunc gIntrTableTemplate[] =
{
VCountIntr, // V-count interrupt
SerialIntr, // Serial interrupt
Timer3Intr, // Timer 3 interrupt
HBlankIntr, // H-blank interrupt
VBlankIntr, // V-blank interrupt
IntrDummy, // Timer 0 interrupt
IntrDummy, // Timer 1 interrupt
IntrDummy, // Timer 2 interrupt
IntrDummy, // DMA 0 interrupt
IntrDummy, // DMA 1 interrupt
IntrDummy, // DMA 2 interrupt
IntrDummy, // DMA 3 interrupt
IntrDummy, // Key interrupt
IntrDummy, // Game Pak interrupt
};
#define INTR_COUNT ((int)(sizeof(gIntrTableTemplate)/sizeof(IntrFunc)))
2017-09-30 15:32:46 +02:00
static u16 gUnknown_03000000;
2017-02-04 01:44:06 +01:00
2017-11-08 05:53:30 +01:00
u16 gKeyRepeatStartDelay;
2017-11-11 06:05:44 +01:00
bool8 gLinkTransferringData;
2017-11-08 05:53:30 +01:00
struct Main gMain;
u16 gKeyRepeatContinueDelay;
bool8 gSoftResetDisabled;
IntrFunc gIntrTable[INTR_COUNT];
2017-11-11 06:05:44 +01:00
u8 gLinkVSyncDisabled;
2017-11-08 05:53:30 +01:00
u32 IntrMain_Buffer[0x200];
2018-11-28 17:14:32 +01:00
s8 gPcmDmaCounter;
2017-02-04 01:44:06 +01:00
2017-11-08 05:53:30 +01:00
static EWRAM_DATA u16 gTrainerId = 0;
2017-02-04 01:44:06 +01:00
2017-11-08 05:53:30 +01:00
//EWRAM_DATA void (**gFlashTimerIntrFunc)(void) = NULL;
2017-02-04 01:44:06 +01:00
static void UpdateLinkAndCallCallbacks(void);
static void InitMainCallbacks(void);
static void CallCallbacks(void);
//static void SeedRngWithRtc(void);
2017-02-04 01:44:06 +01:00
static void ReadKeys(void);
void InitIntrHandlers(void);
2017-02-04 01:44:06 +01:00
static void WaitForVBlank(void);
void EnableVCountIntrAtLine150(void);
2017-02-04 01:44:06 +01:00
#define B_START_SELECT (B_BUTTON | START_BUTTON | SELECT_BUTTON)
void AgbMain()
{
#if MODERN
// Modern compilers are liberal with the stack on entry to this function,
// so RegisterRamReset may crash if it resets IWRAM.
RegisterRamReset(RESET_ALL & ~RESET_IWRAM);
2019-07-08 03:07:30 +02:00
asm("mov\tr1, #0xC0\n"
"\tlsl\tr1, r1, #0x12\n"
"\tmov r2, #0xFC\n"
"\tlsl r2, r2, #0x7\n"
"\tadd\tr2, r1, r2\n"
"\tmov\tr0, #0\n"
"\tmov\tr3, r0\n"
"\tmov\tr4, r0\n"
"\tmov\tr5, r0\n"
".LCU0:\n"
"\tstmia r1!, {r0, r3, r4, r5}\n"
"\tcmp\tr1, r2\n"
"\tbcc\t.LCU0\n"
);
#else
RegisterRamReset(RESET_ALL);
#endif //MODERN
2017-02-04 01:44:06 +01:00
*(vu16 *)BG_PLTT = 0x7FFF;
InitGpuRegManager();
REG_WAITCNT = WAITCNT_PREFETCH_ENABLE | WAITCNT_WS0_S_1 | WAITCNT_WS0_N_3;
InitKeys();
InitIntrHandlers();
m4aSoundInit();
EnableVCountIntrAtLine150();
sub_800E6D0();
RtcInit();
CheckForFlashMemory();
InitMainCallbacks();
InitMapMusic();
//SeedRngWithRtc(); see comment at SeedRngWithRtc declaration below
2017-02-04 01:44:06 +01:00
ClearDma3Requests();
ResetBgs();
SetDefaultFontsPointer();
InitHeap(gHeap, HEAP_SIZE);
2017-02-04 01:44:06 +01:00
gSoftResetDisabled = FALSE;
if (gFlashMemoryPresent != TRUE)
SetMainCallback2(NULL);
2017-11-11 06:05:44 +01:00
gLinkTransferringData = FALSE;
2017-02-04 01:44:06 +01:00
gUnknown_03000000 = 0xFC0;
for (;;)
{
ReadKeys();
if (gSoftResetDisabled == FALSE
&& (gMain.heldKeysRaw & A_BUTTON)
&& (gMain.heldKeysRaw & B_START_SELECT) == B_START_SELECT)
{
rfu_REQ_stopMode();
rfu_waitREQComplete();
DoSoftReset();
}
if (sub_8087634() == 1)
{
2017-11-11 06:05:44 +01:00
gLinkTransferringData = TRUE;
2017-02-04 01:44:06 +01:00
UpdateLinkAndCallCallbacks();
2017-11-11 06:05:44 +01:00
gLinkTransferringData = FALSE;
2017-02-04 01:44:06 +01:00
}
else
{
2017-11-11 06:05:44 +01:00
gLinkTransferringData = FALSE;
2017-02-04 01:44:06 +01:00
UpdateLinkAndCallCallbacks();
if (sub_80875C8() == 1)
{
gMain.newKeys = 0;
2017-09-01 19:43:26 +02:00
ClearSpriteCopyRequests();
2017-11-11 06:05:44 +01:00
gLinkTransferringData = TRUE;
2017-02-04 01:44:06 +01:00
UpdateLinkAndCallCallbacks();
2017-11-11 06:05:44 +01:00
gLinkTransferringData = FALSE;
2017-02-04 01:44:06 +01:00
}
}
PlayTimeCounter_Update();
MapMusicMain();
WaitForVBlank();
}
}
static void UpdateLinkAndCallCallbacks(void)
{
if (!HandleLinkConnection())
CallCallbacks();
}
static void InitMainCallbacks(void)
{
gMain.vblankCounter1 = 0;
2019-03-01 07:49:11 +01:00
gTrainerHillVBlankCounter = NULL;
2017-02-04 01:44:06 +01:00
gMain.vblankCounter2 = 0;
gMain.callback1 = NULL;
2018-02-15 23:54:34 +01:00
SetMainCallback2(CB2_InitCopyrightScreenAfterBootup);
2017-09-04 18:26:39 +02:00
gSaveBlock2Ptr = &gSaveblock2;
2017-09-06 18:39:03 +02:00
gPokemonStoragePtr = &gPokemonStorage;
2017-02-04 01:44:06 +01:00
}
static void CallCallbacks(void)
{
if (gMain.callback1)
gMain.callback1();
if (gMain.callback2)
gMain.callback2();
}
void SetMainCallback2(MainCallback callback)
{
gMain.callback2 = callback;
gMain.state = 0;
}
void StartTimer1(void)
{
REG_TM1CNT_H = 0x80;
}
void SeedRngAndSetTrainerId(void)
{
u16 val = REG_TM1CNT_L;
SeedRng(val);
REG_TM1CNT_H = 0;
gTrainerId = val;
}
2017-09-03 22:50:17 +02:00
u16 GetGeneratedTrainerIdLower(void)
2017-02-04 01:44:06 +01:00
{
return gTrainerId;
}
void EnableVCountIntrAtLine150(void)
{
2017-02-17 19:56:19 +01:00
u16 gpuReg = (GetGpuReg(REG_OFFSET_DISPSTAT) & 0xFF) | (150 << 8);
SetGpuReg(REG_OFFSET_DISPSTAT, gpuReg | DISPSTAT_VCOUNT_INTR);
EnableInterrupts(INTR_FLAG_VCOUNT);
}
// oops! FRLG commented this out to remove RTC, however Emerald didnt undo this!
//static void SeedRngWithRtc(void)
//{
// u32 seed = RtcGetMinuteCount();
// seed = (seed >> 16) ^ (seed & 0xFFFF);
// SeedRng(seed);
//}
2017-02-04 01:44:06 +01:00
void InitKeys(void)
{
gKeyRepeatContinueDelay = 5;
gKeyRepeatStartDelay = 40;
2017-02-17 19:52:03 +01:00
2017-02-04 01:44:06 +01:00
gMain.heldKeys = 0;
gMain.newKeys = 0;
gMain.newAndRepeatedKeys = 0;
gMain.heldKeysRaw = 0;
gMain.newKeysRaw = 0;
}
static void ReadKeys(void)
{
u16 keyInput = REG_KEYINPUT ^ KEYS_MASK;
gMain.newKeysRaw = keyInput & ~gMain.heldKeysRaw;
gMain.newKeys = gMain.newKeysRaw;
gMain.newAndRepeatedKeys = gMain.newKeysRaw;
// BUG: Key repeat won't work when pressing L using L=A button mode
// because it compares the raw key input with the remapped held keys.
// Note that newAndRepeatedKeys is never remapped either.
if (keyInput != 0 && gMain.heldKeys == keyInput)
{
gMain.keyRepeatCounter--;
if (gMain.keyRepeatCounter == 0)
{
gMain.newAndRepeatedKeys = keyInput;
gMain.keyRepeatCounter = gKeyRepeatContinueDelay;
}
}
else
{
// If there is no input or the input has changed, reset the counter.
gMain.keyRepeatCounter = gKeyRepeatStartDelay;
}
gMain.heldKeysRaw = keyInput;
gMain.heldKeys = gMain.heldKeysRaw;
// Remap L to A if the L=A option is enabled.
if (gSaveBlock2Ptr->optionsButtonMode == 2)
2017-02-04 01:44:06 +01:00
{
if (gMain.newKeys & L_BUTTON)
gMain.newKeys |= A_BUTTON;
if (gMain.heldKeys & L_BUTTON)
gMain.heldKeys |= A_BUTTON;
}
if (gMain.newKeys & gMain.watchedKeysMask)
gMain.watchedKeysPressed = TRUE;
}
void InitIntrHandlers(void)
2017-02-04 01:44:06 +01:00
{
int i;
for (i = 0; i < INTR_COUNT; i++)
gIntrTable[i] = gIntrTableTemplate[i];
DmaCopy32(3, IntrMain, IntrMain_Buffer, sizeof(IntrMain_Buffer));
INTR_VECTOR = IntrMain_Buffer;
SetVBlankCallback(NULL);
SetHBlankCallback(NULL);
SetSerialCallback(NULL);
REG_IME = 1;
2017-02-17 19:52:03 +01:00
EnableInterrupts(0x1);
2017-02-04 01:44:06 +01:00
}
void SetVBlankCallback(IntrCallback callback)
{
gMain.vblankCallback = callback;
}
void SetHBlankCallback(IntrCallback callback)
{
gMain.hblankCallback = callback;
}
void SetVCountCallback(IntrCallback callback)
{
gMain.vcountCallback = callback;
}
void RestoreSerialTimer3IntrHandlers(void)
{
gIntrTable[1] = SerialIntr;
gIntrTable[2] = Timer3Intr;
}
2017-02-04 01:44:06 +01:00
void SetSerialCallback(IntrCallback callback)
{
gMain.serialCallback = callback;
}
static void VBlankIntr(void)
{
2017-11-14 14:44:32 +01:00
if (gWirelessCommType != 0)
RfuVSync();
2017-11-11 06:05:44 +01:00
else if (gLinkVSyncDisabled == FALSE)
2017-11-14 14:44:32 +01:00
LinkVSync();
2017-02-04 01:44:06 +01:00
2017-02-17 19:52:03 +01:00
gMain.vblankCounter1++;
2019-03-01 07:49:11 +01:00
if (gTrainerHillVBlankCounter && *gTrainerHillVBlankCounter < 0xFFFFFFFF)
(*gTrainerHillVBlankCounter)++;
2017-02-04 01:44:06 +01:00
if (gMain.vblankCallback)
gMain.vblankCallback();
gMain.vblankCounter2++;
2017-02-17 19:52:03 +01:00
CopyBufferedValuesToGpuRegs();
ProcessDma3Requests();
2017-02-04 01:44:06 +01:00
gPcmDmaCounter = gSoundInfo.pcmDmaCounter;
m4aSoundMain();
sub_8033648();
2017-09-05 20:13:34 +02:00
if (!gMain.inBattle || !(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_FRONTIER | BATTLE_TYPE_RECORDED)))
Random();
2017-02-04 01:44:06 +01:00
sub_800E174();
2017-02-17 19:52:03 +01:00
INTR_CHECK |= INTR_FLAG_VBLANK;
2017-02-04 01:44:06 +01:00
gMain.intrCheck |= INTR_FLAG_VBLANK;
}
2017-09-04 18:26:39 +02:00
void InitFlashTimer(void)
2017-02-04 01:44:06 +01:00
{
SetFlashTimerIntr(2, gIntrTable + 0x7);
2017-02-04 01:44:06 +01:00
}
static void HBlankIntr(void)
{
if (gMain.hblankCallback)
gMain.hblankCallback();
INTR_CHECK |= INTR_FLAG_HBLANK;
gMain.intrCheck |= INTR_FLAG_HBLANK;
}
static void VCountIntr(void)
{
if (gMain.vcountCallback)
gMain.vcountCallback();
m4aSoundVSync();
2017-02-17 19:52:03 +01:00
INTR_CHECK |= INTR_FLAG_VCOUNT;
2017-02-04 01:44:06 +01:00
gMain.intrCheck |= INTR_FLAG_VCOUNT;
}
static void SerialIntr(void)
{
if (gMain.serialCallback)
gMain.serialCallback();
2017-02-17 19:52:03 +01:00
INTR_CHECK |= INTR_FLAG_SERIAL;
2017-02-04 01:44:06 +01:00
gMain.intrCheck |= INTR_FLAG_SERIAL;
}
static void IntrDummy(void)
{}
static void WaitForVBlank(void)
{
gMain.intrCheck &= ~INTR_FLAG_VBLANK;
2017-02-17 19:52:03 +01:00
2017-02-17 19:58:42 +01:00
while (!(gMain.intrCheck & INTR_FLAG_VBLANK))
;
}
2019-03-01 07:49:11 +01:00
void SetTrainerHillVBlankCounter(u32 *counter)
{
2019-03-01 07:49:11 +01:00
gTrainerHillVBlankCounter = counter;
}
2019-03-01 07:49:11 +01:00
void ClearTrainerHillVBlankCounter(void)
{
2019-03-01 07:49:11 +01:00
gTrainerHillVBlankCounter = NULL;
2017-02-04 01:44:06 +01:00
}
void DoSoftReset(void)
{
REG_IME = 0;
m4aSoundVSyncOff();
2018-01-16 18:59:06 +01:00
ScanlineEffect_Stop();
2017-02-04 01:44:06 +01:00
DmaStop(1);
DmaStop(2);
DmaStop(3);
SiiRtcProtect();
SoftReset(RESET_ALL);
}
void ClearPokemonCrySongs(void)
{
CpuFill16(0, gPokemonCrySongs, MAX_POKEMON_CRIES * sizeof(struct PokemonCrySong));
}