435 lines
9.8 KiB
C
435 lines
9.8 KiB
C
|
#include "global.h"
|
||
|
#include "crt0.h"
|
||
|
#include "malloc.h"
|
||
|
#include "link.h"
|
||
|
#include "link_rfu.h"
|
||
|
#include "librfu.h"
|
||
|
#include "m4a.h"
|
||
|
#include "bg.h"
|
||
|
#include "rtc.h"
|
||
|
#include "scanline_effect.h"
|
||
|
#include "overworld.h"
|
||
|
#include "play_time.h"
|
||
|
#include "random.h"
|
||
|
#include "dma3.h"
|
||
|
#include "gba/flash_internal.h"
|
||
|
#include "load_save.h"
|
||
|
#include "gpu_regs.h"
|
||
|
#include "agb_flash.h"
|
||
|
#include "sound.h"
|
||
|
#include "battle.h"
|
||
|
#include "battle_controllers.h"
|
||
|
#include "text.h"
|
||
|
#include "intro.h"
|
||
|
#include "main.h"
|
||
|
#include "trainer_hill.h"
|
||
|
#include "constants/rgb.h"
|
||
|
|
||
|
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;
|
||
|
|
||
|
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)))
|
||
|
|
||
|
static u16 sUnusedVar; // Never read
|
||
|
|
||
|
u16 gKeyRepeatStartDelay;
|
||
|
bool8 gLinkTransferringData;
|
||
|
struct Main gMain;
|
||
|
u16 gKeyRepeatContinueDelay;
|
||
|
bool8 gSoftResetDisabled;
|
||
|
IntrFunc gIntrTable[INTR_COUNT];
|
||
|
u8 gLinkVSyncDisabled;
|
||
|
u32 IntrMain_Buffer[0x200];
|
||
|
s8 gPcmDmaCounter;
|
||
|
|
||
|
static EWRAM_DATA u16 gTrainerId = 0;
|
||
|
|
||
|
//EWRAM_DATA void (**gFlashTimerIntrFunc)(void) = NULL;
|
||
|
|
||
|
static void UpdateLinkAndCallCallbacks(void);
|
||
|
static void InitMainCallbacks(void);
|
||
|
static void CallCallbacks(void);
|
||
|
static void SeedRngWithRtc(void);
|
||
|
static void ReadKeys(void);
|
||
|
void InitIntrHandlers(void);
|
||
|
static void WaitForVBlank(void);
|
||
|
void EnableVCountIntrAtLine150(void);
|
||
|
|
||
|
#define B_START_SELECT (B_BUTTON | START_BUTTON | SELECT_BUTTON)
|
||
|
|
||
|
void AgbMain()
|
||
|
{
|
||
|
// Modern compilers are liberal with the stack on entry to this function,
|
||
|
// so RegisterRamReset may crash if it resets IWRAM.
|
||
|
#if !MODERN
|
||
|
RegisterRamReset(RESET_ALL);
|
||
|
#endif //MODERN
|
||
|
*(vu16 *)BG_PLTT = RGB_WHITE; // Set the backdrop to white on startup
|
||
|
InitGpuRegManager();
|
||
|
REG_WAITCNT = WAITCNT_PREFETCH_ENABLE | WAITCNT_WS0_S_1 | WAITCNT_WS0_N_3;
|
||
|
InitKeys();
|
||
|
InitIntrHandlers();
|
||
|
m4aSoundInit();
|
||
|
EnableVCountIntrAtLine150();
|
||
|
InitRFU();
|
||
|
RtcInit();
|
||
|
CheckForFlashMemory();
|
||
|
InitMainCallbacks();
|
||
|
InitMapMusic();
|
||
|
#ifdef BUGFIX
|
||
|
SeedRngWithRtc(); // see comment at SeedRngWithRtc definition below
|
||
|
#endif
|
||
|
ClearDma3Requests();
|
||
|
ResetBgs();
|
||
|
SetDefaultFontsPointer();
|
||
|
InitHeap(gHeap, HEAP_SIZE);
|
||
|
|
||
|
gSoftResetDisabled = FALSE;
|
||
|
|
||
|
if (gFlashMemoryPresent != TRUE)
|
||
|
SetMainCallback2(NULL);
|
||
|
|
||
|
gLinkTransferringData = FALSE;
|
||
|
sUnusedVar = 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 (Overworld_SendKeysToLinkIsRunning() == TRUE)
|
||
|
{
|
||
|
gLinkTransferringData = TRUE;
|
||
|
UpdateLinkAndCallCallbacks();
|
||
|
gLinkTransferringData = FALSE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
gLinkTransferringData = FALSE;
|
||
|
UpdateLinkAndCallCallbacks();
|
||
|
|
||
|
if (Overworld_RecvKeysFromLinkIsRunning() == TRUE)
|
||
|
{
|
||
|
gMain.newKeys = 0;
|
||
|
ClearSpriteCopyRequests();
|
||
|
gLinkTransferringData = TRUE;
|
||
|
UpdateLinkAndCallCallbacks();
|
||
|
gLinkTransferringData = FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
PlayTimeCounter_Update();
|
||
|
MapMusicMain();
|
||
|
WaitForVBlank();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void UpdateLinkAndCallCallbacks(void)
|
||
|
{
|
||
|
if (!HandleLinkConnection())
|
||
|
CallCallbacks();
|
||
|
}
|
||
|
|
||
|
static void InitMainCallbacks(void)
|
||
|
{
|
||
|
gMain.vblankCounter1 = 0;
|
||
|
gTrainerHillVBlankCounter = NULL;
|
||
|
gMain.vblankCounter2 = 0;
|
||
|
gMain.callback1 = NULL;
|
||
|
SetMainCallback2(CB2_InitCopyrightScreenAfterBootup);
|
||
|
gSaveBlock2Ptr = &gSaveblock2.block;
|
||
|
gPokemonStoragePtr = &gPokemonStorage.block;
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
u16 GetGeneratedTrainerIdLower(void)
|
||
|
{
|
||
|
return gTrainerId;
|
||
|
}
|
||
|
|
||
|
void EnableVCountIntrAtLine150(void)
|
||
|
{
|
||
|
u16 gpuReg = (GetGpuReg(REG_OFFSET_DISPSTAT) & 0xFF) | (150 << 8);
|
||
|
SetGpuReg(REG_OFFSET_DISPSTAT, gpuReg | DISPSTAT_VCOUNT_INTR);
|
||
|
EnableInterrupts(INTR_FLAG_VCOUNT);
|
||
|
}
|
||
|
|
||
|
// FRLG commented this out to remove RTC, however Emerald didn't undo this!
|
||
|
#ifdef BUGFIX
|
||
|
static void SeedRngWithRtc(void)
|
||
|
{
|
||
|
u32 seed = RtcGetMinuteCount();
|
||
|
seed = (seed >> 16) ^ (seed & 0xFFFF);
|
||
|
SeedRng(seed);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
void InitKeys(void)
|
||
|
{
|
||
|
gKeyRepeatContinueDelay = 5;
|
||
|
gKeyRepeatStartDelay = 40;
|
||
|
|
||
|
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 == OPTIONS_BUTTON_MODE_L_EQUALS_A)
|
||
|
{
|
||
|
if (JOY_NEW(L_BUTTON))
|
||
|
gMain.newKeys |= A_BUTTON;
|
||
|
|
||
|
if (JOY_HELD(L_BUTTON))
|
||
|
gMain.heldKeys |= A_BUTTON;
|
||
|
}
|
||
|
|
||
|
if (gMain.newKeys & gMain.watchedKeysMask)
|
||
|
gMain.watchedKeysPressed = TRUE;
|
||
|
}
|
||
|
|
||
|
void InitIntrHandlers(void)
|
||
|
{
|
||
|
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;
|
||
|
|
||
|
EnableInterrupts(INTR_FLAG_VBLANK);
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
void SetSerialCallback(IntrCallback callback)
|
||
|
{
|
||
|
gMain.serialCallback = callback;
|
||
|
}
|
||
|
|
||
|
static void VBlankIntr(void)
|
||
|
{
|
||
|
if (gWirelessCommType != 0)
|
||
|
RfuVSync();
|
||
|
else if (gLinkVSyncDisabled == FALSE)
|
||
|
LinkVSync();
|
||
|
|
||
|
gMain.vblankCounter1++;
|
||
|
|
||
|
if (gTrainerHillVBlankCounter && *gTrainerHillVBlankCounter < 0xFFFFFFFF)
|
||
|
(*gTrainerHillVBlankCounter)++;
|
||
|
|
||
|
if (gMain.vblankCallback)
|
||
|
gMain.vblankCallback();
|
||
|
|
||
|
gMain.vblankCounter2++;
|
||
|
|
||
|
CopyBufferedValuesToGpuRegs();
|
||
|
ProcessDma3Requests();
|
||
|
|
||
|
gPcmDmaCounter = gSoundInfo.pcmDmaCounter;
|
||
|
|
||
|
m4aSoundMain();
|
||
|
TryReceiveLinkBattleData();
|
||
|
|
||
|
if (!gMain.inBattle || !(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_FRONTIER | BATTLE_TYPE_RECORDED)))
|
||
|
Random();
|
||
|
|
||
|
UpdateWirelessStatusIndicatorSprite();
|
||
|
|
||
|
INTR_CHECK |= INTR_FLAG_VBLANK;
|
||
|
gMain.intrCheck |= INTR_FLAG_VBLANK;
|
||
|
}
|
||
|
|
||
|
void InitFlashTimer(void)
|
||
|
{
|
||
|
SetFlashTimerIntr(2, gIntrTable + 0x7);
|
||
|
}
|
||
|
|
||
|
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();
|
||
|
INTR_CHECK |= INTR_FLAG_VCOUNT;
|
||
|
gMain.intrCheck |= INTR_FLAG_VCOUNT;
|
||
|
}
|
||
|
|
||
|
static void SerialIntr(void)
|
||
|
{
|
||
|
if (gMain.serialCallback)
|
||
|
gMain.serialCallback();
|
||
|
|
||
|
INTR_CHECK |= INTR_FLAG_SERIAL;
|
||
|
gMain.intrCheck |= INTR_FLAG_SERIAL;
|
||
|
}
|
||
|
|
||
|
static void IntrDummy(void)
|
||
|
{}
|
||
|
|
||
|
static void WaitForVBlank(void)
|
||
|
{
|
||
|
gMain.intrCheck &= ~INTR_FLAG_VBLANK;
|
||
|
|
||
|
while (!(gMain.intrCheck & INTR_FLAG_VBLANK))
|
||
|
;
|
||
|
}
|
||
|
|
||
|
void SetTrainerHillVBlankCounter(u32 *counter)
|
||
|
{
|
||
|
gTrainerHillVBlankCounter = counter;
|
||
|
}
|
||
|
|
||
|
void ClearTrainerHillVBlankCounter(void)
|
||
|
{
|
||
|
gTrainerHillVBlankCounter = NULL;
|
||
|
}
|
||
|
|
||
|
void DoSoftReset(void)
|
||
|
{
|
||
|
REG_IME = 0;
|
||
|
m4aSoundVSyncOff();
|
||
|
ScanlineEffect_Stop();
|
||
|
DmaStop(1);
|
||
|
DmaStop(2);
|
||
|
DmaStop(3);
|
||
|
SiiRtcProtect();
|
||
|
SoftReset(RESET_ALL);
|
||
|
}
|
||
|
|
||
|
void ClearPokemonCrySongs(void)
|
||
|
{
|
||
|
CpuFill16(0, gPokemonCrySongs, MAX_POKEMON_CRIES * sizeof(struct PokemonCrySong));
|
||
|
}
|