diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/battle_ai.c | 2838 | ||||
-rw-r--r-- | src/dma3_manager.c | 528 | ||||
-rw-r--r-- | src/main.c | 418 | ||||
-rw-r--r-- | src/multiboot.c | 470 | ||||
-rw-r--r-- | src/rng.c | 35 | ||||
-rw-r--r-- | src/siirtc.c | 432 | ||||
-rw-r--r-- | src/string_util.c | 780 | ||||
-rw-r--r-- | src/task.c | 111 |
8 files changed, 5511 insertions, 101 deletions
diff --git a/src/battle_ai.c b/src/battle_ai.c new file mode 100644 index 000000000..8f3b7cf1e --- /dev/null +++ b/src/battle_ai.c @@ -0,0 +1,2838 @@ +#include "global.h" +#include "pokemon.h" +#include "battle.h" +#include "species.h" +#include "abilities.h" + +#define AIScriptRead32(ptr) ((ptr)[0] | (ptr)[1] << 8 | (ptr)[2] << 16 | (ptr)[3] << 24) +#define AIScriptRead16(ptr) ((ptr)[0] | (ptr)[1] << 8) +#define AIScriptRead8(ptr) ((ptr)[0]) +#define AIScriptReadPtr(ptr) (u8*) AIScriptRead32(ptr) + +#define AI_THINKING_STRUCT ((struct AI_ThinkingStruct *)(gUnknown_020244A8->ai)) +#define UNK_2016A00_STRUCT ((struct UnknownStruct2 *)(gUnknown_020244A8->unk18)) +#define UNK_2016C00_STRUCT ((struct UnknownStruct4 *)(gUnknown_020244A8->unk1C)) +#define UNK_BATTLE_STRUCT ((struct UnknownStruct1 *)(gUnknown_020244A8)) + +#define AI_ACTION_UNK1 0x0001 +#define AI_ACTION_UNK2 0x0002 +#define AI_ACTION_UNK3 0x0004 +#define AI_ACTION_UNK4 0x0008 +#define AI_ACTION_UNK5 0x0010 +#define AI_ACTION_UNK6 0x0020 +#define AI_ACTION_UNK7 0x0040 +#define AI_ACTION_UNK8 0x0080 + +enum +{ + TARGET, + USER +}; + +// AI states +enum +{ + AIState_SettingUp, + AIState_Processing, + AIState_FinishedProcessing, + AIState_DoNotProcess +}; + +//Copied from pokeruby -- hopefully the same +struct Trainer +{ + /*0x00*/ u8 partyFlags; + /*0x01*/ u8 trainerClass; + /*0x02*/ u8 encounterMusic:7; + /*0x02*/ u8 gender:1; + /*0x03*/ u8 trainerPic; + /*0x04*/ u8 trainerName[12]; + /*0x10*/ u16 items[4]; + /*0x18*/ bool8 doubleBattle; + /*0x1C*/ u32 aiFlags; + /*0x20*/ u8 partySize; + /*0x24*/ void *party; +}; + +#define POKEMON_NAME_LENGTH 10 + +struct BattlePokemon +{ + /* 0x00 */ u16 species; + /* 0x02 */ u16 attack; + /* 0x04 */ u16 defense; + /* 0x06 */ u16 speed; + /* 0x08 */ u16 spAttack; + /* 0x0A */ u16 spDefense; + /* 0x0C */ u16 moves[4]; + /* 0x14 */ u32 hpIV:5; + /* 0x14 */ u32 attackIV:5; + /* 0x15 */ u32 defenseIV:5; + /* 0x15 */ u32 speedIV:5; + /* 0x16 */ u32 spAttackIV:5; + /* 0x17 */ u32 spDefenseIV:5; + /* 0x17 */ u32 isEgg:1; + /* 0x17 */ u32 altAbility:1; + /* 0x18 */ s8 statStages[8]; + /* 0x20 */ u8 ability; + /* 0x21 */ u8 type1; + /* 0x22 */ u8 type2; + /* 0x23 */ u8 unknown; + /* 0x24 */ u8 pp[4]; + /* 0x28 */ u16 hp; + /* 0x2A */ u8 level; + /* 0x2B */ u8 friendship; + /* 0x2C */ u16 maxHP; + /* 0x2E */ u16 item; + /* 0x30 */ u8 nickname[POKEMON_NAME_LENGTH + 1]; + /* 0x3B */ u8 ppBonuses; + /* 0x3C */ u8 otName[8]; + /* 0x44 */ u32 experience; + /* 0x48 */ u32 personality; + /* 0x4C */ u32 status1; + /* 0x50 */ u32 status2; + /* 0x54 */ u32 otId; +}; + +//size should be 0x1C +struct AI_ThinkingStruct +{ + u8 aiState; + u8 movesetIndex; + u16 moveConsidered; + s8 score[4]; + u32 funcResult; + u32 aiFlags; + u8 aiAction; + u8 aiLogicId; + u8 filler12[6]; + u8 unk18[4]; +}; + +//size should be 0x54 +struct UnknownStruct2 +{ + u16 unk0[2][8]; + u8 unk20[2]; + u8 filler20[0x1E]; + u8 unk40[4]; + u8 unk44[4]; + u16 unk48[4]; + u8 unk50; +}; + +struct UnknownStruct4 +{ + u8 *ptr[8]; + u8 unk20; +}; + +struct SimpleUnknownStruct +{ + u32 unkArray[4]; // unknown size +}; + +struct UnknownStruct1 +{ + u8 unk0; + u8 filler1[0x3]; + struct SimpleUnknownStruct *unk4; + u8 filler8[0xC]; + struct AI_ThinkingStruct *ai; + struct UnknownStruct2 *unk18; + struct UnknownStruct4 *unk1C; +}; + +struct UnknownStruct5 +{ + u8 filler0[0x3]; + u16 unk4; + u16 unk6; + u8 unk8; + u8 unk9; + u8 fillerA[0x9]; + u8 taunt:4; + u8 unkC:4; + u8 fillerD[0x2]; + u8 unk16; + u8 filler17[0x4]; +}; + +extern struct UnknownStruct5 gUnknown_020242BC[]; + +/* +gAIScriptPtr is a pointer to the next battle AI cmd command to read. +when a command finishes processing, gAIScriptPtr is incremented by +the number of bytes that the current command had reserved for arguments +in order to read the next command correctly. refer to battle_ai_scripts.s for the +AI scripts. +*/ +extern u8 *gAIScriptPtr; + +extern u32 gBattleTypeFlags; +extern u8 gUnknown_02024064; +extern struct BattlePokemon gBattleMons[]; +extern u16 gUnknown_020241EA; +extern u8 gEnemyMonIndex; +extern u8 gUnknown_02024210; +extern u16 gUnknown_02024248[]; +extern u8 *gUnknown_0202449C; +extern struct UnknownStruct1 *gUnknown_020244A8; +extern u16 gUnknown_02038BCA; +extern u16 gUnknown_02038BCC; +extern u8 gPlayerMonIndex; +extern struct Trainer gTrainers[]; +extern const u32 gBitTable[]; +extern u8 *gUnknown_082DBEF8[]; +extern u32 gUnknown_020242AC[]; +extern u16 gUnknown_0202428E[]; +extern struct BattleMove gBattleMoves[]; +extern u8 gUnknown_03005D10[]; +extern u8 gUnknown_0202406E[][2]; +extern struct BaseStats gBaseStats[]; +extern u16 gUnknown_02024400; +extern u8 gUnknown_02024474[]; +extern u8 gBattleMoveFlags; +extern int gBattleMoveDamage; +extern u8 gCritMultiplier; +extern u16 gBattleWeather; + +extern u8 battle_get_per_side_status(u8); +extern u8 b_first_side(u8, u8, u8); +extern u8 battle_get_side_with_given_state(u8); +extern void move_effectiveness_something(u16, u8, u8); +extern u8 itemid_get_x12(); +extern void b_mc_stack_push(u8 *); +extern bool8 b_mc_stack_pop_cursor(void); +extern void sub_8046E7C(u8, u8); + +typedef void (*BattleAICmdFunc)(void); + +extern const BattleAICmdFunc gUnknown_085B083C[]; + +extern u8 sub_803FECC(); +extern u16 Random(); +extern u8 battle_side_get_owner(); +extern u32 sub_8186438(); +extern u32 sub_81A6FB4(); + +void BattleAI_SetupAIData(u8 a); +u8 BattleAI_GetAIActionToUse(void); +u8 sub_8130CF4(void); +void sub_8131074(void); +void BattleAI_DoAIProcessing(void); + +void BattleAI_HandleItemUseBeforeAISetup(u8 a) +{ + s32 i; + u8 *data = (u8 *)gUnknown_020244A8->unk18; + + for (i = 0; (u32)i < 0x54; i++) + data[i] = 0; + if ((gBattleTypeFlags & 0x0A7F098A) == 8) + { + for (i = 0; i < 4; i++) + { + if (gTrainers[gUnknown_02038BCA].items[i] != 0) + { + gUnknown_020244A8->unk18->unk48[gUnknown_020244A8->unk18->unk50] = gTrainers[gUnknown_02038BCA].items[i]; + gUnknown_020244A8->unk18->unk50++; + } + } + } + + BattleAI_SetupAIData(a); +} + +void BattleAI_SetupAIData(u8 a) +{ + s32 i; + u8 *data = (u8 *)AI_THINKING_STRUCT; + u8 r6; + + // clear AI data. + for (i = 0; (u32)i < sizeof(struct AI_ThinkingStruct); i++) + data[i] = 0; + + // conditional score reset, unlike Ruby. + for (i = 0; i < 4; i++) + { + if (a & 1) + AI_THINKING_STRUCT->score[i] = 100; + else + AI_THINKING_STRUCT->score[i] = 0; + a >>= 1; + } + + r6 = sub_803FECC(gUnknown_02024064, 0, 0xFF); + + for (i = 0; i < 4; i++) + { + if (gBitTable[i] & r6) + AI_THINKING_STRUCT->score[i] = 0; + + AI_THINKING_STRUCT->unk18[i] = 100 - (Random() % 16); + } + gUnknown_020244A8->unk1C->unk20 = 0; + gPlayerMonIndex = gUnknown_02024064; + if (gBattleTypeFlags & 1) + { + gEnemyMonIndex = (Random() & 2) + ((u32)battle_side_get_owner(gUnknown_02024064) ^ 1); + if (gUnknown_02024210 & gBitTable[gEnemyMonIndex]) + gEnemyMonIndex ^= 2; + } + else + { + //_08130A60 + gEnemyMonIndex = gPlayerMonIndex ^ 1; + } + //_08130A68 + if (gBattleTypeFlags & 0x1000000) + AI_THINKING_STRUCT->aiFlags = sub_8186438(); + else if (gBattleTypeFlags & 0x80) + AI_THINKING_STRUCT->aiFlags = 0x40000000; + else if (gBattleTypeFlags & 0x400) + AI_THINKING_STRUCT->aiFlags = 0x20000000; + else if (gBattleTypeFlags & 0x10) + AI_THINKING_STRUCT->aiFlags = 0x80000000; + else if (gBattleTypeFlags & 0x80000) + AI_THINKING_STRUCT->aiFlags = sub_81A6FB4(); + else if (gBattleTypeFlags & 0x0C3F0900) + AI_THINKING_STRUCT->aiFlags = 7; + else if (gBattleTypeFlags & 0x8000) + AI_THINKING_STRUCT->aiFlags = gTrainers[gUnknown_02038BCA].aiFlags | gTrainers[gUnknown_02038BCC].aiFlags; + else + AI_THINKING_STRUCT->aiFlags = gTrainers[gUnknown_02038BCA].aiFlags; + if (gBattleTypeFlags & 1) + AI_THINKING_STRUCT->aiFlags |= 0x80; +} + +u8 sub_8130BA4(void) +{ + u16 r4 = gUnknown_020241EA; + u8 ret; + + if (!(gBattleTypeFlags & 1)) + ret = BattleAI_GetAIActionToUse(); + else + ret = sub_8130CF4(); + + gUnknown_020241EA = r4; + return ret; +} + +u8 BattleAI_GetAIActionToUse(void) +{ + u8 currentMoveArray[4]; + u8 consideredMoveArray[4]; + u8 numOfBestMoves; + s32 i; + + sub_8131074(); + + while (AI_THINKING_STRUCT->aiFlags != 0) + { + if (AI_THINKING_STRUCT->aiFlags & 1) + { + AI_THINKING_STRUCT->aiState = AIState_SettingUp; + BattleAI_DoAIProcessing(); + } + AI_THINKING_STRUCT->aiFlags >>= 1; + AI_THINKING_STRUCT->aiLogicId++; + AI_THINKING_STRUCT->movesetIndex = 0; + } + + // special flags for safari watch/flee. + if (AI_THINKING_STRUCT->aiAction & 2) + return 4; + if (AI_THINKING_STRUCT->aiAction & 4) + return 5; + + numOfBestMoves = 1; + currentMoveArray[0] = AI_THINKING_STRUCT->score[0]; + consideredMoveArray[0] = 0; + + for (i = 1; i < 4; i++) + { + if (gBattleMons[gPlayerMonIndex].moves[i] != 0) // emerald adds an extra move ID check for some reason. + { + // in ruby, the order of these if statements are reversed. + if (currentMoveArray[0] == AI_THINKING_STRUCT->score[i]) + { + currentMoveArray[numOfBestMoves] = AI_THINKING_STRUCT->score[i]; + consideredMoveArray[numOfBestMoves++] = i; + } + if (currentMoveArray[0] < AI_THINKING_STRUCT->score[i]) + { + numOfBestMoves = 1; + currentMoveArray[0] = AI_THINKING_STRUCT->score[i]; + consideredMoveArray[0] = i; + } + } + } + return consideredMoveArray[Random() % numOfBestMoves]; +} + +#ifdef NONMATCHING +u8 sub_8130CF4(void) +{ + s32 i; + s32 j; + //s32 r4_2; + #define r4_2 r4 + s32 r5; + s16 r5_2; + s32 r4; + s16 sp0[4]; + s8 sp8[4]; + s8 spC[4]; + u8 sp10[4]; // definitely unsigned + u8 sp14[4]; + //u8 *sp1C = spC; + //u8 *sp18 = sp8; + //u8 *sp20 = spC; + + for (i = 0; i < 4; i++) //_08130D14 + { + if (i == gPlayerMonIndex || gBattleMons[i].hp == 0) + { + //_08130D2E + spC[i] = -1; + sp0[i] = -1; + } + //_08130D48 + else + { + if (gBattleTypeFlags & 0x20000) + BattleAI_SetupAIData(gUnknown_0202449C[0x92] >> 4); + else + BattleAI_SetupAIData(0xF); + //_08130D76 + gEnemyMonIndex = i; + if ((i & 1) != (gPlayerMonIndex & 1)) + sub_8131074(); + //_08130D90 + AI_THINKING_STRUCT->unk11 = 0; + AI_THINKING_STRUCT->unk1 = 0; + r4 = AI_THINKING_STRUCT->aiFlags; + while (r4 != 0) + { + if (r4 & 1) + { + AI_THINKING_STRUCT->aiState = AIState_SettingUp; + BattleAI_DoAIProcessing(); + } + r4 >>= 1; + AI_THINKING_STRUCT->unk11++; + AI_THINKING_STRUCT->unk1 = 0; + } + //_08130DD8 + if (AI_THINKING_STRUCT->unk10 & 2) + spC[i] = 4; + else if (AI_THINKING_STRUCT->unk10 & 4) + spC[i] = 5; + else + { + //_08130E10 + sp10[0] = AI_THINKING_STRUCT->score[0]; + sp14[0] = 0; + r5 = 1; + for (j = 1; j < 4; j++) + { + if (gBattleMons[gPlayerMonIndex].moves[j] != 0) + { + if (sp10[0] == AI_THINKING_STRUCT->score[j]) + { + sp10[r5] = AI_THINKING_STRUCT->score[j]; + sp14[r5] = j; + r5++; + } + if (sp10[0] < AI_THINKING_STRUCT->score[j]) + { + sp10[0] = AI_THINKING_STRUCT->score[j]; + sp14[0] = j; + r5 = 1; + } + } + //_08130E72 + } + spC[i] = sp14[Random() % r5]; + //asm("":::"r3"); + sp0[i] = sp10[0]; + if (i == (gPlayerMonIndex ^ 2) && sp0[i] < 100) + sp0[i] = -1; + } + } + //_08130EAE + } + + //#define i r5 + + //_08130EC4 + r5_2 = sp0[0]; + sp8[0] = 0; + r4_2 = 1; + for (i = 1; i < 4; i++) + { + //_08130EDA + if (r5_2 == sp0[i]) + { + sp8[r4_2] = i; + r4_2++; + } + //_08130EEE + if (r5_2 < sp0[i]) + { + r5_2 = sp0[i]; + sp8[0] = i; + r4_2 = 1; + } + } + gEnemyMonIndex = sp8[Random() % r4_2]; + return spC[gEnemyMonIndex]; +} +#else +__attribute__((naked)) +u8 sub_8130CF4(void) +{ + asm(".syntax unified\n\ + push {r4-r7,lr}\n\ + mov r7, r10\n\ + mov r6, r9\n\ + mov r5, r8\n\ + push {r5-r7}\n\ + sub sp, 0x24\n\ + movs r0, 0\n\ + mov r8, r0\n\ + mov r1, sp\n\ + adds r1, 0xC\n\ + str r1, [sp, 0x1C]\n\ + mov r2, sp\n\ + adds r2, 0x8\n\ + str r2, [sp, 0x18]\n\ + str r1, [sp, 0x20]\n\ + mov r10, sp\n\ +_08130D14:\n\ + ldr r0, =gPlayerMonIndex\n\ + ldrb r0, [r0]\n\ + cmp r8, r0\n\ + beq _08130D2E\n\ + movs r0, 0x58\n\ + mov r7, r8\n\ + muls r7, r0\n\ + adds r0, r7, 0\n\ + ldr r1, =gBattleMons\n\ + adds r0, r1\n\ + ldrh r0, [r0, 0x28]\n\ + cmp r0, 0\n\ + bne _08130D48\n\ +_08130D2E:\n\ + movs r0, 0xFF\n\ + ldr r2, [sp, 0x20]\n\ + strb r0, [r2]\n\ + ldr r0, =0x0000ffff\n\ + mov r7, r10\n\ + strh r0, [r7]\n\ + b _08130EAE\n\ + .pool\n\ +_08130D48:\n\ + ldr r0, =gBattleTypeFlags\n\ + ldr r0, [r0]\n\ + movs r1, 0x80\n\ + lsls r1, 10\n\ + ands r0, r1\n\ + cmp r0, 0\n\ + beq _08130D70\n\ + ldr r0, =gUnknown_0202449C\n\ + ldr r0, [r0]\n\ + adds r0, 0x92\n\ + ldrb r0, [r0]\n\ + lsrs r0, 4\n\ + bl BattleAI_SetupAIData\n\ + b _08130D76\n\ + .pool\n\ +_08130D70:\n\ + movs r0, 0xF\n\ + bl BattleAI_SetupAIData\n\ +_08130D76:\n\ + ldr r0, =gEnemyMonIndex\n\ + mov r1, r8\n\ + strb r1, [r0]\n\ + movs r1, 0x1\n\ + mov r2, r8\n\ + ands r2, r1\n\ + ldr r0, =gPlayerMonIndex\n\ + ldrb r0, [r0]\n\ + ands r1, r0\n\ + cmp r2, r1\n\ + beq _08130D90\n\ + bl sub_8131074\n\ +_08130D90:\n\ + ldr r2, =gUnknown_020244A8\n\ + ldr r0, [r2]\n\ + ldr r0, [r0, 0x14]\n\ + movs r1, 0\n\ + strb r1, [r0, 0x11]\n\ + ldr r0, [r2]\n\ + ldr r0, [r0, 0x14]\n\ + strb r1, [r0, 0x1]\n\ + ldr r0, [r2]\n\ + ldr r0, [r0, 0x14]\n\ + ldr r4, [r0, 0xC]\n\ + mov r9, r2\n\ + cmp r4, 0\n\ + beq _08130DD8\n\ + mov r5, r9\n\ + movs r6, 0\n\ +_08130DB0:\n\ + movs r0, 0x1\n\ + ands r0, r4\n\ + cmp r0, 0\n\ + beq _08130DC2\n\ + ldr r0, [r5]\n\ + ldr r0, [r0, 0x14]\n\ + strb r6, [r0]\n\ + bl BattleAI_DoAIProcessing\n\ +_08130DC2:\n\ + asrs r4, 1\n\ + ldr r0, [r5]\n\ + ldr r1, [r0, 0x14]\n\ + ldrb r0, [r1, 0x11]\n\ + adds r0, 0x1\n\ + strb r0, [r1, 0x11]\n\ + ldr r0, [r5]\n\ + ldr r0, [r0, 0x14]\n\ + strb r6, [r0, 0x1]\n\ + cmp r4, 0\n\ + bne _08130DB0\n\ +_08130DD8:\n\ + mov r2, r9\n\ + ldr r0, [r2]\n\ + ldr r3, [r0, 0x14]\n\ + ldrb r1, [r3, 0x10]\n\ + movs r0, 0x2\n\ + ands r0, r1\n\ + cmp r0, 0\n\ + beq _08130DFC\n\ + movs r0, 0x4\n\ + ldr r7, [sp, 0x20]\n\ + strb r0, [r7]\n\ + b _08130EAE\n\ + .pool\n\ +_08130DFC:\n\ + movs r0, 0x4\n\ + ands r0, r1\n\ + lsls r0, 24\n\ + lsrs r2, r0, 24\n\ + cmp r2, 0\n\ + beq _08130E10\n\ + movs r0, 0x5\n\ + ldr r1, [sp, 0x20]\n\ + strb r0, [r1]\n\ + b _08130EAE\n\ +_08130E10:\n\ + add r1, sp, 0x10\n\ + ldrb r0, [r3, 0x4]\n\ + strb r0, [r1]\n\ + add r0, sp, 0x14\n\ + strb r2, [r0]\n\ + movs r5, 0x1\n\ + movs r3, 0x1\n\ + adds r6, r1, 0\n\ + ldr r0, =gPlayerMonIndex\n\ + ldrb r1, [r0]\n\ + movs r0, 0x58\n\ + muls r0, r1\n\ + ldr r2, =gUnknown_02024090\n\ + adds r0, r2\n\ + adds r4, r0, 0x2\n\ + add r7, sp, 0x14\n\ +_08130E30:\n\ + ldrh r0, [r4]\n\ + cmp r0, 0\n\ + beq _08130E72\n\ + ldrb r1, [r6]\n\ + mov r2, r9\n\ + ldr r0, [r2]\n\ + ldr r0, [r0, 0x14]\n\ + adds r0, 0x4\n\ + adds r2, r0, r3\n\ + movs r0, 0\n\ + ldrsb r0, [r2, r0]\n\ + cmp r1, r0\n\ + bne _08130E56\n\ + adds r0, r6, r5\n\ + ldrb r1, [r2]\n\ + strb r1, [r0]\n\ + adds r0, r7, r5\n\ + strb r3, [r0]\n\ + adds r5, 0x1\n\ +_08130E56:\n\ + ldrb r1, [r6]\n\ + mov r2, r9\n\ + ldr r0, [r2]\n\ + ldr r0, [r0, 0x14]\n\ + adds r0, 0x4\n\ + adds r2, r0, r3\n\ + movs r0, 0\n\ + ldrsb r0, [r2, r0]\n\ + cmp r1, r0\n\ + bge _08130E72\n\ + ldrb r0, [r2]\n\ + strb r0, [r6]\n\ + strb r3, [r7]\n\ + movs r5, 0x1\n\ +_08130E72:\n\ + adds r4, 0x2\n\ + adds r3, 0x1\n\ + cmp r3, 0x3\n\ + ble _08130E30\n\ + bl Random\n\ + lsls r0, 16\n\ + lsrs r0, 16\n\ + adds r1, r5, 0\n\ + bl __modsi3\n\ + add r0, sp\n\ + adds r0, 0x14\n\ + ldrb r0, [r0]\n\ + ldr r7, [sp, 0x20]\n\ + strb r0, [r7]\n\ + ldrb r2, [r6]\n\ + mov r0, r10\n\ + strh r2, [r0]\n\ + ldr r0, =gPlayerMonIndex\n\ + ldrb r1, [r0]\n\ + movs r0, 0x2\n\ + eors r0, r1\n\ + cmp r8, r0\n\ + bne _08130EAE\n\ + cmp r2, 0x63\n\ + bgt _08130EAE\n\ + ldr r0, =0x0000ffff\n\ + mov r1, r10\n\ + strh r0, [r1]\n\ +_08130EAE:\n\ + ldr r2, [sp, 0x20]\n\ + adds r2, 0x1\n\ + str r2, [sp, 0x20]\n\ + movs r7, 0x2\n\ + add r10, r7\n\ + movs r0, 0x1\n\ + add r8, r0\n\ + mov r1, r8\n\ + cmp r1, 0x3\n\ + bgt _08130EC4\n\ + b _08130D14\n\ +_08130EC4:\n\ + mov r0, sp\n\ + ldrh r5, [r0]\n\ + movs r0, 0\n\ + ldr r2, [sp, 0x18]\n\ + strb r0, [r2]\n\ + movs r4, 0x1\n\ + mov r8, r4\n\ + ldr r6, =gEnemyMonIndex\n\ + ldr r3, [sp, 0x18]\n\ + mov r1, sp\n\ + adds r1, 0x2\n\ +_08130EDA:\n\ + lsls r0, r5, 16\n\ + asrs r2, r0, 16\n\ + movs r7, 0\n\ + ldrsh r0, [r1, r7]\n\ + cmp r2, r0\n\ + bne _08130EEE\n\ + adds r0, r3, r4\n\ + mov r7, r8\n\ + strb r7, [r0]\n\ + adds r4, 0x1\n\ +_08130EEE:\n\ + movs r7, 0\n\ + ldrsh r0, [r1, r7]\n\ + cmp r2, r0\n\ + bge _08130EFE\n\ + ldrh r5, [r1]\n\ + mov r0, r8\n\ + strb r0, [r3]\n\ + movs r4, 0x1\n\ +_08130EFE:\n\ + adds r1, 0x2\n\ + movs r2, 0x1\n\ + add r8, r2\n\ + mov r7, r8\n\ + cmp r7, 0x3\n\ + ble _08130EDA\n\ + bl Random\n\ + lsls r0, 16\n\ + lsrs r0, 16\n\ + adds r1, r4, 0\n\ + bl __modsi3\n\ + ldr r1, [sp, 0x18]\n\ + adds r0, r1, r0\n\ + ldrb r0, [r0]\n\ + strb r0, [r6]\n\ + ldrb r0, [r6]\n\ + ldr r2, [sp, 0x1C]\n\ + adds r0, r2, r0\n\ + ldrb r0, [r0]\n\ + add sp, 0x24\n\ + pop {r3-r5}\n\ + mov r8, r3\n\ + mov r9, r4\n\ + mov r10, r5\n\ + pop {r4-r7}\n\ + pop {r1}\n\ + bx r1\n\ + .pool\n\ + .syntax divided\n"); +} +#endif + +void BattleAI_DoAIProcessing(void) +{ + while (AI_THINKING_STRUCT->aiState != AIState_FinishedProcessing) + { + switch (AI_THINKING_STRUCT->aiState) + { + case AIState_DoNotProcess: //Needed to match. + break; + case AIState_SettingUp: + gAIScriptPtr = gUnknown_082DBEF8[AI_THINKING_STRUCT->aiLogicId]; // set AI ptr to logic ID. + if (gBattleMons[gPlayerMonIndex].pp[AI_THINKING_STRUCT->movesetIndex] == 0) + { + AI_THINKING_STRUCT->moveConsidered = 0; + } + else + { + AI_THINKING_STRUCT->moveConsidered = gBattleMons[gPlayerMonIndex].moves[AI_THINKING_STRUCT->movesetIndex]; + } + AI_THINKING_STRUCT->aiState++; + break; + case AIState_Processing: + if (AI_THINKING_STRUCT->moveConsidered != 0) + gUnknown_085B083C[*gAIScriptPtr](); // run AI command. + else + { + AI_THINKING_STRUCT->score[AI_THINKING_STRUCT->movesetIndex] = 0; + AI_THINKING_STRUCT->aiAction |= 1; + } + if (AI_THINKING_STRUCT->aiAction & 1) + { + AI_THINKING_STRUCT->movesetIndex++; + + if (AI_THINKING_STRUCT->movesetIndex < 4 && !(AI_THINKING_STRUCT->aiAction & 8)) + AI_THINKING_STRUCT->aiState = AIState_SettingUp; + else + AI_THINKING_STRUCT->aiState++; + + AI_THINKING_STRUCT->aiAction &= 0xFE; + } + break; + } + } +} + +void sub_8131074(void) +{ + s32 i; + + for (i = 0; i < 4; i++) + { + if (gUnknown_020244A8->unk18->unk0[gEnemyMonIndex][i] == gUnknown_02024248[gEnemyMonIndex]) + break; + if (gUnknown_020244A8->unk18->unk0[gEnemyMonIndex][i] != gUnknown_02024248[gEnemyMonIndex] //HACK: This redundant condition is a hack to make the asm match. + && gUnknown_020244A8->unk18->unk0[gEnemyMonIndex][i] == 0) + { + gUnknown_020244A8->unk18->unk0[gEnemyMonIndex][i] = gUnknown_02024248[gEnemyMonIndex]; + break; + } + } +} + +void sub_81310F0(u8 a) +{ + s32 i; + + for (i = 0; i < 4; i++) + gUnknown_020244A8->unk18->unk0[a][i] = 0; +} + +void b_history__record_ability_usage_of_player(u8 a, u8 b) +{ + gUnknown_020244A8->unk18->unk40[a] = b; +} + +void sub_8131130(u8 a) +{ + gUnknown_020244A8->unk18->unk40[a] = 0; +} + +void b_history__record_item_x12_of_player(u8 a, u8 b) +{ + gUnknown_020244A8->unk18->unk44[a] = b; +} + +void sub_8131160(u8 a) +{ + gUnknown_020244A8->unk18->unk44[a] = 0; +} + +static void BattleAICmd_if_random_less_than(void) +{ + u16 random = Random(); + + if (random % 256 < gAIScriptPtr[1]) + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 2); + else + gAIScriptPtr += 6; +} + +static void BattleAICmd_if_random_greater_than(void) +{ + u16 random = Random(); + + if (random % 256 > gAIScriptPtr[1]) + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 2); + else + gAIScriptPtr += 6; +} + +static void BattleAICmd_if_random_equal(void) +{ + u16 random = Random(); + + if (random % 256 == gAIScriptPtr[1]) + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 2); + else + gAIScriptPtr += 6; +} + +static void BattleAICmd_if_random_not_equal(void) +{ + u16 random = Random(); + + if (random % 256 != gAIScriptPtr[1]) + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 2); + else + gAIScriptPtr += 6; +} + +static void BattleAICmd_score(void) +{ + AI_THINKING_STRUCT->score[AI_THINKING_STRUCT->movesetIndex] += gAIScriptPtr[1]; // add the result to the array of the move consider's score. + + if (AI_THINKING_STRUCT->score[AI_THINKING_STRUCT->movesetIndex] < 0) // if the score is negative, flatten it to 0. + AI_THINKING_STRUCT->score[AI_THINKING_STRUCT->movesetIndex] = 0; + + gAIScriptPtr += 2; // AI return. +} + +static void BattleAICmd_if_hp_less_than(void) +{ + u16 index; + + if (gAIScriptPtr[1] == USER) + index = gPlayerMonIndex; + else + index = gEnemyMonIndex; + + if ((u32)(100 * gBattleMons[index].hp / gBattleMons[index].maxHP) < gAIScriptPtr[2]) + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 3); + else + gAIScriptPtr += 7; +} + +static void BattleAICmd_if_hp_more_than(void) +{ + u16 index; + + if (gAIScriptPtr[1] == USER) + index = gPlayerMonIndex; + else + index = gEnemyMonIndex; + + if ((u32)(100 * gBattleMons[index].hp / gBattleMons[index].maxHP) > gAIScriptPtr[2]) + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 3); + else + gAIScriptPtr += 7; +} + +static void BattleAICmd_if_hp_equal(void) +{ + u16 index; + + if (gAIScriptPtr[1] == USER) + index = gPlayerMonIndex; + else + index = gEnemyMonIndex; + + if ((u32)(100 * gBattleMons[index].hp / gBattleMons[index].maxHP) == gAIScriptPtr[2]) + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 3); + else + gAIScriptPtr += 7; +} + +static void BattleAICmd_if_hp_not_equal(void) +{ + u16 index; + + if (gAIScriptPtr[1] == USER) + index = gPlayerMonIndex; + else + index = gEnemyMonIndex; + + if ((u32)(100 * gBattleMons[index].hp / gBattleMons[index].maxHP) != gAIScriptPtr[2]) + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 3); + else + gAIScriptPtr += 7; +} + +static void BattleAICmd_if_status(void) +{ + u16 index; + u32 arg; + + if (gAIScriptPtr[1] == USER) + index = gPlayerMonIndex; + else + index = gEnemyMonIndex; + + arg = AIScriptRead32(gAIScriptPtr + 2); + + if ((gBattleMons[index].status1 & arg) != 0) + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 6); + else + gAIScriptPtr += 10; +} + +static void BattleAICmd_if_not_status(void) +{ + u16 index; + u32 arg; + + if (gAIScriptPtr[1] == USER) + index = gPlayerMonIndex; + else + index = gEnemyMonIndex; + + arg = AIScriptRead32(gAIScriptPtr + 2); + + if ((gBattleMons[index].status1 & arg) == 0) + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 6); + else + gAIScriptPtr += 10; +} + +static void BattleAICmd_if_status2(void) +{ + u16 index; + u32 arg; + + if (gAIScriptPtr[1] == USER) + index = gPlayerMonIndex; + else + index = gEnemyMonIndex; + + arg = AIScriptRead32(gAIScriptPtr + 2); + + if ((gBattleMons[index].status2 & arg) != 0) + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 6); + else + gAIScriptPtr += 10; +} + +static void BattleAICmd_if_not_status2(void) +{ + u16 index; + u32 arg; + + if (gAIScriptPtr[1] == USER) + index = gPlayerMonIndex; + else + index = gEnemyMonIndex; + + arg = AIScriptRead32(gAIScriptPtr + 2); + + if ((gBattleMons[index].status2 & arg) == 0) + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 6); + else + gAIScriptPtr += 10; +} + +static void BattleAICmd_if_status3(void) +{ + u16 index; + u32 arg; + + if (gAIScriptPtr[1] == USER) + index = gPlayerMonIndex; + else + index = gEnemyMonIndex; + + arg = AIScriptRead32(gAIScriptPtr + 2); + + if ((gUnknown_020242AC[index] & arg) != 0) + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 6); + else + gAIScriptPtr += 10; +} + +static void BattleAICmd_if_not_status3(void) +{ + u16 index; + u32 arg; + + if (gAIScriptPtr[1] == USER) + index = gPlayerMonIndex; + else + index = gEnemyMonIndex; + + arg = AIScriptRead32(gAIScriptPtr + 2); + + if ((gUnknown_020242AC[index] & arg) == 0) + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 6); + else + gAIScriptPtr += 10; +} + +static void BattleAICmd_if_status4(void) +{ + u16 index; + u32 arg1, arg2; + + if (gAIScriptPtr[1] == USER) + index = gPlayerMonIndex; + else + index = gEnemyMonIndex; + + arg1 = battle_get_per_side_status(index) & 1; + arg2 = AIScriptRead32(gAIScriptPtr + 2); + + if ((gUnknown_0202428E[arg1] & arg2) != 0) + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 6); + else + gAIScriptPtr += 10; +} + +static void BattleAICmd_if_not_status4(void) +{ + u16 index; + u32 arg1, arg2; + + if (gAIScriptPtr[1] == USER) + index = gPlayerMonIndex; + else + index = gEnemyMonIndex; + + arg1 = battle_get_per_side_status(index) & 1; + arg2 = AIScriptRead32(gAIScriptPtr + 2); + + if ((gUnknown_0202428E[arg1] & arg2) == 0) + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 6); + else + gAIScriptPtr += 10; +} + +static void BattleAICmd_if_less_than(void) +{ + if (AI_THINKING_STRUCT->funcResult < gAIScriptPtr[1]) + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 2); + else + gAIScriptPtr += 6; +} + +static void BattleAICmd_if_more_than(void) +{ + if (AI_THINKING_STRUCT->funcResult > gAIScriptPtr[1]) + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 2); + else + gAIScriptPtr += 6; +} + +static void BattleAICmd_if_equal(void) +{ + if (AI_THINKING_STRUCT->funcResult == gAIScriptPtr[1]) + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 2); + else + gAIScriptPtr += 6; +} + +static void BattleAICmd_if_not_equal(void) +{ + if (AI_THINKING_STRUCT->funcResult != gAIScriptPtr[1]) + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 2); + else + gAIScriptPtr += 6; +} + +static void BattleAICmd_if_less_than_32(void) +{ + u8 *temp = AIScriptReadPtr(gAIScriptPtr + 1); + + if (AI_THINKING_STRUCT->funcResult < *temp) + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 5); + else + gAIScriptPtr += 9; +} + +static void BattleAICmd_if_more_than_32(void) +{ + u8 *temp = AIScriptReadPtr(gAIScriptPtr + 1); + + if (AI_THINKING_STRUCT->funcResult > *temp) + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 5); + else + gAIScriptPtr += 9; +} + +static void BattleAICmd_if_equal_32(void) +{ + u8 *temp = AIScriptReadPtr(gAIScriptPtr + 1); + + if (AI_THINKING_STRUCT->funcResult == *temp) + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 5); + else + gAIScriptPtr += 9; +} + +static void BattleAICmd_if_not_equal_32(void) +{ + u8 *temp = AIScriptReadPtr(gAIScriptPtr + 1); + + if (AI_THINKING_STRUCT->funcResult != *temp) + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 5); + else + gAIScriptPtr += 9; +} + +static void BattleAICmd_if_move(void) +{ + u16 move = AIScriptRead16(gAIScriptPtr + 1); + + if (AI_THINKING_STRUCT->moveConsidered == move) + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 3); + else + gAIScriptPtr += 7; +} + +static void BattleAICmd_if_not_move(void) +{ + u16 move = AIScriptRead16(gAIScriptPtr + 1); + + if (AI_THINKING_STRUCT->moveConsidered != move) + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 3); + else + gAIScriptPtr += 7; +} + +static void BattleAICmd_if_in_bytes(void) +{ + u8 *ptr = AIScriptReadPtr(gAIScriptPtr + 1); + + while (*ptr != 0xFF) + { + if (AI_THINKING_STRUCT->funcResult == *ptr) + { + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 5); + return; + } + ptr++; + } + gAIScriptPtr += 9; +} + +static void BattleAICmd_if_not_in_bytes(void) +{ + u8 *ptr = AIScriptReadPtr(gAIScriptPtr + 1); + + while (*ptr != 0xFF) + { + if (AI_THINKING_STRUCT->funcResult == *ptr) + { + gAIScriptPtr += 9; + return; + } + ptr++; + } + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 5); +} + +static void BattleAICmd_if_in_words(void) +{ + u16 *ptr = (u16 *)AIScriptReadPtr(gAIScriptPtr + 1); + + while (*ptr != 0xFFFF) + { + if (AI_THINKING_STRUCT->funcResult == *ptr) + { + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 5); + return; + } + ptr++; + } + gAIScriptPtr += 9; +} + +static void BattleAICmd_if_not_in_words(void) +{ + u16 *ptr = (u16 *)AIScriptReadPtr(gAIScriptPtr + 1); + + while (*ptr != 0xFFFF) + { + if (AI_THINKING_STRUCT->funcResult == *ptr) + { + gAIScriptPtr += 9; + return; + } + ptr++; + } + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 5); +} + +static void BattleAICmd_if_user_can_damage(void) +{ + s32 i; + + for (i = 0; i < 4; i++) + { + if (gBattleMons[gPlayerMonIndex].moves[i] != 0 + && gBattleMoves[gBattleMons[gPlayerMonIndex].moves[i]].power != 0) + break; + } + if (i == 4) + gAIScriptPtr += 5; + else + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 1); +} + +static void BattleAICmd_if_user_cant_damage(void) +{ + s32 i; + + for (i = 0; i < 4; i++) + { + if (gBattleMons[gPlayerMonIndex].moves[i] != 0 + && gBattleMoves[gBattleMons[gPlayerMonIndex].moves[i]].power != 0) + break; + } + if (i != 4) + gAIScriptPtr += 5; + else + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 1); +} + +static void BattleAICmd_get_turn_count(void) +{ + AI_THINKING_STRUCT->funcResult = gUnknown_03005D10[19]; + gAIScriptPtr += 1; +} + +static void BattleAICmd_get_type(void) +{ + u8 typeVar = gAIScriptPtr[1]; + + switch (typeVar) + { + case 1: // player primary type + AI_THINKING_STRUCT->funcResult = gBattleMons[gPlayerMonIndex].type1; + break; + case 0: // enemy primary type + AI_THINKING_STRUCT->funcResult = gBattleMons[gEnemyMonIndex].type1; + break; + case 3: // player secondary type + AI_THINKING_STRUCT->funcResult = gBattleMons[gPlayerMonIndex].type2; + break; + case 2: // enemy secondary type + AI_THINKING_STRUCT->funcResult = gBattleMons[gEnemyMonIndex].type2; + break; + case 4: // type of move being pointed to + AI_THINKING_STRUCT->funcResult = gBattleMoves[AI_THINKING_STRUCT->moveConsidered].type; + break; + } + gAIScriptPtr += 2; +} + +// util for double battles? whats this doing in the middle of the battle AI macros? +u8 sub_8131E70(u8 index) +{ + switch (index) + { + case 1: + return gPlayerMonIndex; + case 0: + default: + return gEnemyMonIndex; + case 3: + return gPlayerMonIndex ^ 2; + case 2: + return gEnemyMonIndex ^ 2; + } +} + +static void BattleAICmd_unk_5F(void) +{ + u8 index = sub_8131E70(gAIScriptPtr[1]); + + if(gBattleMons[index].type1 == gAIScriptPtr[2] || gBattleMons[index].type2 == gAIScriptPtr[2]) + { + AI_THINKING_STRUCT->funcResult = 1; + } + else + { + AI_THINKING_STRUCT->funcResult = 0; + } + + gAIScriptPtr += 3; +} + +static void BattleAICmd_get_move_power(void) +{ + AI_THINKING_STRUCT->funcResult = gBattleMoves[AI_THINKING_STRUCT->moveConsidered].power; + gAIScriptPtr += 1; +} + +__attribute__((naked)) // not even going to try. if it doesnt match in ruby, it wont match in emerald (yet). +static void BattleAICmd_is_most_powerful_move(void) +{ + asm(".syntax unified\n\ + push {r4-r7,lr}\n\ + mov r7, r10\n\ + mov r6, r9\n\ + mov r5, r8\n\ + push {r5-r7}\n\ + sub sp, 0x14\n\ + movs r3, 0\n\ + ldr r0, =gUnknown_085B09C8\n\ + ldrh r1, [r0]\n\ + ldr r5, =0x0000ffff\n\ + ldr r6, =gBattleMoves\n\ + ldr r2, =gUnknown_020244A8\n\ + cmp r1, r5\n\ + beq _08131F86\n\ + ldr r0, [r2]\n\ + ldr r0, [r0, 0x14]\n\ + ldrh r1, [r0, 0x2]\n\ + lsls r0, r1, 1\n\ + adds r0, r1\n\ + lsls r0, 2\n\ + adds r0, r6\n\ + ldrb r4, [r0]\n\ + ldr r1, =gUnknown_085B09C8\n\ +_08131F76:\n\ + ldrh r0, [r1]\n\ + cmp r4, r0\n\ + beq _08131F86\n\ + adds r1, 0x2\n\ + adds r3, 0x1\n\ + ldrh r0, [r1]\n\ + cmp r0, r5\n\ + bne _08131F76\n\ +_08131F86:\n\ + ldr r0, [r2]\n\ + ldr r0, [r0, 0x14]\n\ + ldrh r1, [r0, 0x2]\n\ + lsls r0, r1, 1\n\ + adds r0, r1\n\ + lsls r0, 2\n\ + adds r0, r6\n\ + ldrb r0, [r0, 0x1]\n\ + cmp r0, 0x1\n\ + bhi _08131F9C\n\ + b _08132126\n\ +_08131F9C:\n\ + lsls r0, r3, 1\n\ + ldr r1, =gUnknown_085B09C8\n\ + adds r0, r1\n\ + ldrh r3, [r0]\n\ + ldr r0, =0x0000ffff\n\ + cmp r3, r0\n\ + beq _08131FAC\n\ + b _08132126\n\ +_08131FAC:\n\ + ldr r0, =gUnknown_02024400\n\ + movs r1, 0\n\ + strh r1, [r0]\n\ + ldr r0, =gUnknown_0202449C\n\ + ldr r0, [r0]\n\ + strb r1, [r0, 0x13]\n\ + ldr r0, =gUnknown_02024474\n\ + movs r2, 0x1\n\ + strb r2, [r0, 0xE]\n\ + ldr r0, =gBattleMoveFlags\n\ + strb r1, [r0]\n\ + ldr r0, =gCritMultiplier\n\ + strb r2, [r0]\n\ + movs r6, 0\n\ + mov r9, r3\n\ + ldr r2, =gUnknown_085B09C8\n\ + ldrh r2, [r2]\n\ + str r2, [sp, 0x10]\n\ +_08131FD0:\n\ + movs r3, 0\n\ + ldr r5, =gBattleMons\n\ + lsls r4, r6, 1\n\ + ldr r7, =gPlayerMonIndex\n\ + lsls r0, r6, 2\n\ + mov r8, r0\n\ + adds r1, r6, 0x1\n\ + mov r10, r1\n\ + ldr r2, [sp, 0x10]\n\ + cmp r2, r9\n\ + beq _08132014\n\ + ldr r2, =gBattleMoves\n\ + ldrb r1, [r7]\n\ + movs r0, 0x58\n\ + muls r0, r1\n\ + adds r0, r4, r0\n\ + adds r1, r5, 0\n\ + adds r1, 0xC\n\ + adds r0, r1\n\ + ldrh r1, [r0]\n\ + lsls r0, r1, 1\n\ + adds r0, r1\n\ + lsls r0, 2\n\ + adds r0, r2\n\ + ldrb r2, [r0]\n\ + ldr r1, =gUnknown_085B09C8\n\ +_08132004:\n\ + ldrh r0, [r1]\n\ + cmp r2, r0\n\ + beq _08132014\n\ + adds r1, 0x2\n\ + adds r3, 0x1\n\ + ldrh r0, [r1]\n\ + cmp r0, r9\n\ + bne _08132004\n\ +_08132014:\n\ + ldrb r1, [r7]\n\ + movs r0, 0x58\n\ + muls r0, r1\n\ + adds r0, r4, r0\n\ + adds r1, r5, 0\n\ + adds r1, 0xC\n\ + adds r1, r0, r1\n\ + ldrh r0, [r1]\n\ + cmp r0, 0\n\ + beq _081320C0\n\ + lsls r0, r3, 1\n\ + ldr r2, =gUnknown_085B09C8\n\ + adds r0, r2\n\ + ldrh r0, [r0]\n\ + cmp r0, r9\n\ + bne _081320C0\n\ + ldr r0, =gBattleMoves\n\ + ldrh r2, [r1]\n\ + lsls r1, r2, 1\n\ + adds r1, r2\n\ + lsls r1, 2\n\ + adds r1, r0\n\ + ldrb r0, [r1, 0x1]\n\ + cmp r0, 0x1\n\ + bls _081320C0\n\ + ldr r5, =gUnknown_020241EA\n\ + strh r2, [r5]\n\ + ldrb r0, [r7]\n\ + ldr r4, =gEnemyMonIndex\n\ + ldrb r1, [r4]\n\ + bl sub_8046E7C\n\ + ldrh r0, [r5]\n\ + ldrb r1, [r7]\n\ + ldrb r2, [r4]\n\ + bl move_effectiveness_something\n\ + mov r4, sp\n\ + add r4, r8\n\ + ldr r2, =gBattleMoveDamage\n\ + ldr r0, =gUnknown_020244A8\n\ + ldr r0, [r0]\n\ + ldr r0, [r0, 0x14]\n\ + adds r0, 0x18\n\ + adds r0, r6\n\ + ldrb r1, [r0]\n\ + ldr r0, [r2]\n\ + muls r0, r1\n\ + movs r1, 0x64\n\ + bl __divsi3\n\ + str r0, [r4]\n\ + cmp r0, 0\n\ + bne _081320C8\n\ + movs r0, 0x1\n\ + str r0, [r4]\n\ + b _081320C8\n\ + .pool\n\ +_081320C0:\n\ + mov r1, sp\n\ + add r1, r8\n\ + movs r0, 0\n\ + str r0, [r1]\n\ +_081320C8:\n\ + mov r6, r10\n\ + cmp r6, 0x3\n\ + bgt _081320D0\n\ + b _08131FD0\n\ +_081320D0:\n\ + movs r6, 0\n\ + ldr r2, =gUnknown_020244A8\n\ + ldr r0, [r2]\n\ + ldr r0, [r0, 0x14]\n\ + ldrb r0, [r0, 0x1]\n\ + lsls r0, 2\n\ + add r0, sp\n\ + ldr r1, [sp]\n\ + ldr r0, [r0]\n\ + ldr r5, =gAIScriptPtr\n\ + cmp r1, r0\n\ + bgt _08132106\n\ + adds r4, r2, 0\n\ + mov r3, sp\n\ +_081320EC:\n\ + adds r3, 0x4\n\ + adds r6, 0x1\n\ + cmp r6, 0x3\n\ + bgt _08132106\n\ + ldr r0, [r4]\n\ + ldr r0, [r0, 0x14]\n\ + ldrb r0, [r0, 0x1]\n\ + lsls r0, 2\n\ + add r0, sp\n\ + ldr r1, [r3]\n\ + ldr r0, [r0]\n\ + cmp r1, r0\n\ + ble _081320EC\n\ +_08132106:\n\ + cmp r6, 0x4\n\ + bne _0813211C\n\ + ldr r0, [r2]\n\ + ldr r1, [r0, 0x14]\n\ + movs r0, 0x2\n\ + str r0, [r1, 0x8]\n\ + b _08132130\n\ + .pool\n\ +_0813211C:\n\ + ldr r0, [r2]\n\ + ldr r1, [r0, 0x14]\n\ + movs r0, 0x1\n\ + str r0, [r1, 0x8]\n\ + b _08132130\n\ +_08132126:\n\ + ldr r0, [r2]\n\ + ldr r1, [r0, 0x14]\n\ + movs r0, 0\n\ + str r0, [r1, 0x8]\n\ + ldr r5, =gAIScriptPtr\n\ +_08132130:\n\ + ldr r0, [r5]\n\ + adds r0, 0x1\n\ + str r0, [r5]\n\ + add sp, 0x14\n\ + pop {r3-r5}\n\ + mov r8, r3\n\ + mov r9, r4\n\ + mov r10, r5\n\ + pop {r4-r7}\n\ + pop {r0}\n\ + bx r0\n\ + .pool\n\ + .syntax divided"); +} + +static void BattleAICmd_get_move(void) +{ + if (gAIScriptPtr[1] == USER) + AI_THINKING_STRUCT->funcResult = gUnknown_02024248[gPlayerMonIndex]; + else + AI_THINKING_STRUCT->funcResult = gUnknown_02024248[gEnemyMonIndex]; + + gAIScriptPtr += 2; +} + +static void BattleAICmd_if_arg_equal(void) +{ + if (gAIScriptPtr[1] == AI_THINKING_STRUCT->funcResult) + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 2); + else + gAIScriptPtr += 6; +} + +static void BattleAICmd_if_arg_not_equal(void) +{ + if (gAIScriptPtr[1] != AI_THINKING_STRUCT->funcResult) + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 2); + else + gAIScriptPtr += 6; +} + +static void BattleAICmd_if_would_go_first(void) +{ + if (b_first_side(gPlayerMonIndex, gEnemyMonIndex, 1) == gAIScriptPtr[1]) + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 2); + else + gAIScriptPtr += 6; +} + +static void BattleAICmd_if_would_not_go_first(void) +{ + if (b_first_side(gPlayerMonIndex, gEnemyMonIndex, 1) != gAIScriptPtr[1]) + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 2); + else + gAIScriptPtr += 6; +} + +static void BattleAICmd_nullsub_2A(void) +{ +} + +static void BattleAICmd_nullsub_2B(void) +{ +} + +static void BattleAICmd_count_alive_pokemon(void) +{ + u8 index; + u8 var, var2; + struct Pokemon *party; + int i; + + AI_THINKING_STRUCT->funcResult = 0; + + if (gAIScriptPtr[1] == USER) + index = gPlayerMonIndex; + else + index = gEnemyMonIndex; + + if (battle_side_get_owner(index) == 0) + party = gPlayerParty; + else + party = gEnemyParty; + + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) + { + u32 status; + var = gUnknown_0202406E[index][0]; + status = battle_get_per_side_status(index) ^ 2; + var2 = gUnknown_0202406E[battle_get_side_with_given_state(status)][0]; + } + else + { + var = gUnknown_0202406E[index][0]; + var2 = gUnknown_0202406E[index][0]; + } + + for (i = 0; i < 6; i++) + { + if (i != var && i != var2 + && GetMonData(&party[i], MON_DATA_HP) != 0 + && GetMonData(&party[i], MON_DATA_SPECIES2) != SPECIES_NONE + && GetMonData(&party[i], MON_DATA_SPECIES2) != SPECIES_EGG) + { + AI_THINKING_STRUCT->funcResult++; + } + } + + gAIScriptPtr += 2; +} + +static void BattleAICmd_get_considered_move(void) +{ + AI_THINKING_STRUCT->funcResult = AI_THINKING_STRUCT->moveConsidered; + gAIScriptPtr += 1; +} + +static void BattleAICmd_get_considered_move_effect(void) +{ + AI_THINKING_STRUCT->funcResult = gBattleMoves[AI_THINKING_STRUCT->moveConsidered].effect; + gAIScriptPtr += 1; +} + +static void BattleAICmd_get_ability(void) +{ + u8 index; + + if (gAIScriptPtr[1] == USER) + index = gPlayerMonIndex; + else + index = gEnemyMonIndex; + + if(gUnknown_02024064 != index) + { + if(UNK_2016A00_STRUCT->unk40[index] != 0) + { + AI_THINKING_STRUCT->funcResult = UNK_2016A00_STRUCT->unk40[index]; + gAIScriptPtr += 2; + return; + } + + // abilities that prevent fleeing. + if (gBattleMons[index].ability == ABILITY_SHADOW_TAG + || gBattleMons[index].ability == ABILITY_MAGNET_PULL + || gBattleMons[index].ability == ABILITY_ARENA_TRAP) + { + AI_THINKING_STRUCT->funcResult = gBattleMons[index].ability; + gAIScriptPtr += 2; + return; + } + + if (gBaseStats[gBattleMons[index].species].ability1 != ABILITY_NONE) + { + if (gBaseStats[gBattleMons[index].species].ability2 != ABILITY_NONE) + { + // AI has no knowledge of opponent, so it guesses which ability. + if(Random() & 1) + { + AI_THINKING_STRUCT->funcResult = gBaseStats[gBattleMons[index].species].ability1; + } + else + { + AI_THINKING_STRUCT->funcResult = gBaseStats[gBattleMons[index].species].ability2; + } + } + else + { + AI_THINKING_STRUCT->funcResult = gBaseStats[gBattleMons[index].species].ability1; // it's definitely ability 1. + } + } + else + { + AI_THINKING_STRUCT->funcResult = gBaseStats[gBattleMons[index].species].ability2; // AI cant actually reach this part since every mon has at least 1 ability. + } + } + else + { + // The AI knows its own ability. + AI_THINKING_STRUCT->funcResult = gBattleMons[index].ability; + } + gAIScriptPtr += 2; +} + +#ifdef NONMATCHING +static void tai60_unk(void) +{ + u8 index = sub_8131E70(gAIScriptPtr[1]); + u8 arg2 = gAIScriptPtr[2]; + u8 var; + + if(gAIScriptPtr[1] == 0 || gAIScriptPtr[1] == 2) + { + // _0813253A + if(UNK_2016A00_STRUCT->unk40[index] != 0) + { + var = UNK_2016A00_STRUCT->unk40[index]; + AI_THINKING_STRUCT->funcResult = var; + } + else + { + // _0813255C + if (gBattleMons[index].ability == ABILITY_SHADOW_TAG + || gBattleMons[index].ability == ABILITY_MAGNET_PULL + || gBattleMons[index].ability == ABILITY_ARENA_TRAP) + { + var = gBattleMons[index].ability; + } + else + { + // _08132588 + if (gBaseStats[gBattleMons[index].species].ability1 != ABILITY_NONE) + { + if (gBaseStats[gBattleMons[index].species].ability2 != ABILITY_NONE) + { + if(gBaseStats[gBattleMons[index].species].ability1 != arg2 && gBaseStats[gBattleMons[index].species].ability2 != arg2) + { + var = 2; + } + else + { + var = gBaseStats[gBattleMons[index].species].ability1; + } + } + else + { + // _081325B4 + var = gBaseStats[gBattleMons[index].species].ability1; + } + } + else + { + // _081325B8 + var = gBaseStats[gBattleMons[index].species].ability2; + } + } + } + } + else + { + // _081325BC + var = gBattleMons[index].ability; + } + + // _081325CA + if(var == ABILITY_NONE) + { + AI_THINKING_STRUCT->funcResult = 2; + } + else if(var == arg2) + { + AI_THINKING_STRUCT->funcResult = 1; + } + else + { + AI_THINKING_STRUCT->funcResult = 0; + } + gAIScriptPtr += 3; +} +#else +__attribute__((naked)) +static void tai60_unk(void) +{ + asm(".syntax unified\n\ + push {r4-r6,lr}\n\ + ldr r4, =gAIScriptPtr\n\ + ldr r0, [r4]\n\ + ldrb r0, [r0, 0x1]\n\ + bl sub_8131E70\n\ + lsls r0, 24\n\ + lsrs r5, r0, 24\n\ + ldr r0, [r4]\n\ + ldrb r3, [r0, 0x2]\n\ + ldrb r0, [r0, 0x1]\n\ + cmp r0, 0\n\ + beq _0813253A\n\ + cmp r0, 0x2\n\ + bne _081325BC\n\ +_0813253A:\n\ + ldr r0, =gUnknown_020244A8\n\ + ldr r4, [r0]\n\ + ldr r1, [r4, 0x18]\n\ + adds r1, 0x40\n\ + adds r2, r1, r5\n\ + ldrb r1, [r2]\n\ + adds r6, r0, 0\n\ + cmp r1, 0\n\ + beq _0813255C\n\ + adds r3, r1, 0\n\ + ldr r0, [r4, 0x14]\n\ + str r3, [r0, 0x8]\n\ + b _081325CA\n\ + .pool\n\ +_0813255C:\n\ + ldr r1, =gBattleMons\n\ + movs r0, 0x58\n\ + muls r0, r5\n\ + adds r4, r0, r1\n\ + adds r0, r4, 0\n\ + adds r0, 0x20\n\ + ldrb r0, [r0]\n\ + cmp r0, 0x17\n\ + beq _08132576\n\ + cmp r0, 0x2A\n\ + beq _08132576\n\ + cmp r0, 0x47\n\ + bne _08132588\n\ +_08132576:\n\ + movs r0, 0x58\n\ + muls r0, r5\n\ + adds r0, r1\n\ + adds r0, 0x20\n\ + ldrb r3, [r0]\n\ + b _081325CA\n\ + .pool\n\ +_08132588:\n\ + ldr r2, =gBaseStats\n\ + ldrh r1, [r4]\n\ + lsls r0, r1, 3\n\ + subs r0, r1\n\ + lsls r0, 2\n\ + adds r1, r0, r2\n\ + ldrb r4, [r1, 0x16]\n\ + cmp r4, 0\n\ + beq _081325B8\n\ + ldrb r2, [r1, 0x17]\n\ + cmp r2, 0\n\ + beq _081325B4\n\ + adds r0, r3, 0\n\ + cmp r4, r0\n\ + beq _081325CE\n\ + cmp r2, r0\n\ + beq _081325CE\n\ + adds r3, r4, 0\n\ + b _081325CA\n\ + .pool\n\ +_081325B4:\n\ + ldrb r3, [r1, 0x16]\n\ + b _081325CA\n\ +_081325B8:\n\ + ldrb r3, [r1, 0x17]\n\ + b _081325CA\n\ +_081325BC:\n\ + ldr r1, =gBattleMons\n\ + movs r0, 0x58\n\ + muls r0, r5\n\ + adds r0, r1\n\ + adds r0, 0x20\n\ + ldrb r3, [r0]\n\ + ldr r6, =gUnknown_020244A8\n\ +_081325CA:\n\ + cmp r3, 0\n\ + bne _081325E8\n\ +_081325CE:\n\ + ldr r0, [r6]\n\ + ldr r1, [r0, 0x14]\n\ + movs r0, 0x2\n\ + str r0, [r1, 0x8]\n\ + ldr r2, =gAIScriptPtr\n\ + b _08132608\n\ + .pool\n\ +_081325E8:\n\ + ldr r0, =gAIScriptPtr\n\ + ldr r1, [r0]\n\ + adds r2, r0, 0\n\ + ldrb r1, [r1, 0x2]\n\ + cmp r3, r1\n\ + bne _08132600\n\ + ldr r0, [r6]\n\ + ldr r1, [r0, 0x14]\n\ + movs r0, 0x1\n\ + b _08132606\n\ + .pool\n\ +_08132600:\n\ + ldr r0, [r6]\n\ + ldr r1, [r0, 0x14]\n\ + movs r0, 0\n\ +_08132606:\n\ + str r0, [r1, 0x8]\n\ +_08132608:\n\ + ldr r0, [r2]\n\ + adds r0, 0x3\n\ + str r0, [r2]\n\ + pop {r4-r6}\n\ + pop {r0}\n\ + bx r0\n\ + .pool\n\ + .syntax divided"); +} +#endif + +static void BattleAICmd_get_highest_possible_damage(void) +{ + s32 i; + + gUnknown_02024400 = 0; + gUnknown_0202449C[0x13] = 0; + gUnknown_02024474[0xE] = 1; + gBattleMoveFlags = 0; + gCritMultiplier = 1; + AI_THINKING_STRUCT->funcResult = 0; + + for (i = 0; i < 4; i++) + { + gBattleMoveDamage = 40; + gUnknown_020241EA = gBattleMons[gPlayerMonIndex].moves[i]; + + if (gUnknown_020241EA) + { + move_effectiveness_something(gUnknown_020241EA, gPlayerMonIndex, gEnemyMonIndex); + + // reduce by 1/3. + if (gBattleMoveDamage == 120) + gBattleMoveDamage = 80; + if (gBattleMoveDamage == 240) + gBattleMoveDamage = 160; + if (gBattleMoveDamage == 30) + gBattleMoveDamage = 20; + if (gBattleMoveDamage == 15) + gBattleMoveDamage = 10; + + if (gBattleMoveFlags & 8) // if it's a status move, it wont do anything. + gBattleMoveDamage = 0; + + if (AI_THINKING_STRUCT->funcResult < gBattleMoveDamage) + AI_THINKING_STRUCT->funcResult = gBattleMoveDamage; + } + } + gAIScriptPtr += 1; +} + +static void BattleAICmd_if_damage_bonus(void) +{ + u8 damageVar; + + gUnknown_02024400 = 0; + gUnknown_0202449C[0x13] = 0; + gUnknown_02024474[0xE] = 1; + gBattleMoveFlags = 0; + gCritMultiplier = 1; + + gBattleMoveDamage = 40; + gUnknown_020241EA = AI_THINKING_STRUCT->moveConsidered; + + move_effectiveness_something(gUnknown_020241EA, gPlayerMonIndex, gEnemyMonIndex); + + if (gBattleMoveDamage == 120) + gBattleMoveDamage = 80; + if (gBattleMoveDamage == 240) + gBattleMoveDamage = 160; + if (gBattleMoveDamage == 30) + gBattleMoveDamage = 20; + if (gBattleMoveDamage == 15) + gBattleMoveDamage = 10; + + if (gBattleMoveFlags & 8) + gBattleMoveDamage = 0; + + // store gBattleMoveDamage in a u8 variable because gAIScriptPtr[1] is a u8. + damageVar = gBattleMoveDamage; + + if (damageVar == gAIScriptPtr[1]) + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 2); + else + gAIScriptPtr += 6; +} + +static void BattleAICmd_nullsub_32(void) +{ +} + +static void BattleAICmd_nullsub_33(void) +{ +} + +static void BattleAICmd_if_status_in_party(void) +{ + struct Pokemon *party; + int i; + u32 statusToCompareTo; + u8 index; + + switch(gAIScriptPtr[1]) + { + case 1: + index = gPlayerMonIndex; + break; + default: + index = gEnemyMonIndex; + break; + } + + party = (battle_side_get_owner(index) == 0) ? gPlayerParty : gEnemyParty; + + statusToCompareTo = AIScriptRead32(gAIScriptPtr + 2); + + for (i = 0; i < 6; i++) + { + u16 species = GetMonData(&party[i], MON_DATA_SPECIES); + u16 hp = GetMonData(&party[i], MON_DATA_HP); + u32 status = GetMonData(&party[i], MON_DATA_STATUS); + + if (species != SPECIES_NONE && species != SPECIES_EGG && hp != 0 && status == statusToCompareTo) + { + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 6); + return; + } + } + + gAIScriptPtr += 10; +} + +static void BattleAICmd_if_status_not_in_party(void) +{ + struct Pokemon *party; + int i; + u32 statusToCompareTo; + u8 index; + + switch(gAIScriptPtr[1]) + { + case 1: + index = gPlayerMonIndex; + break; + default: + index = gEnemyMonIndex; + break; + } + + party = (battle_side_get_owner(index) == 0) ? gPlayerParty : gEnemyParty; + + statusToCompareTo = AIScriptRead32(gAIScriptPtr + 2); + + for (i = 0; i < 6; i++) + { + u16 species = GetMonData(&party[i], MON_DATA_SPECIES); + u16 hp = GetMonData(&party[i], MON_DATA_HP); + u32 status = GetMonData(&party[i], MON_DATA_STATUS); + + if (species != SPECIES_NONE && species != SPECIES_EGG && hp != 0 && status == statusToCompareTo) + { + gAIScriptPtr += 10; // still bugged in Emerald + } + } + + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 6); +} + +static void BattleAICmd_get_weather(void) +{ + if (gBattleWeather & 7) + AI_THINKING_STRUCT->funcResult = 1; + if (gBattleWeather & 0x18) + AI_THINKING_STRUCT->funcResult = 2; + if (gBattleWeather & 0x60) + AI_THINKING_STRUCT->funcResult = 0; + if (gBattleWeather & 0x80) + AI_THINKING_STRUCT->funcResult = 3; + + gAIScriptPtr += 1; +} + +static void BattleAICmd_if_effect(void) +{ + if (gBattleMoves[AI_THINKING_STRUCT->moveConsidered].effect == gAIScriptPtr[1]) + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 2); + else + gAIScriptPtr += 6; +} + +static void BattleAICmd_if_not_effect(void) +{ + if (gBattleMoves[AI_THINKING_STRUCT->moveConsidered].effect != gAIScriptPtr[1]) + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 2); + else + gAIScriptPtr += 6; +} + +static void BattleAICmd_if_stat_level_less_than(void) +{ + u32 party; + + if (gAIScriptPtr[1] == USER) + party = gPlayerMonIndex; + else + party = gEnemyMonIndex; + + if (gBattleMons[party].statStages[gAIScriptPtr[2]] < gAIScriptPtr[3]) + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 4); + else + gAIScriptPtr += 8; +} + +static void BattleAICmd_if_stat_level_more_than(void) +{ + u32 party; + + if (gAIScriptPtr[1] == USER) + party = gPlayerMonIndex; + else + party = gEnemyMonIndex; + + if (gBattleMons[party].statStages[gAIScriptPtr[2]] > gAIScriptPtr[3]) + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 4); + else + gAIScriptPtr += 8; +} + +static void BattleAICmd_if_stat_level_equal(void) +{ + u32 party; + + if (gAIScriptPtr[1] == USER) + party = gPlayerMonIndex; + else + party = gEnemyMonIndex; + + if (gBattleMons[party].statStages[gAIScriptPtr[2]] == gAIScriptPtr[3]) + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 4); + else + gAIScriptPtr += 8; +} + +static void BattleAICmd_if_stat_level_not_equal(void) +{ + u32 party; + + if (gAIScriptPtr[1] == USER) + party = gPlayerMonIndex; + else + party = gEnemyMonIndex; + + if (gBattleMons[party].statStages[gAIScriptPtr[2]] != gAIScriptPtr[3]) + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 4); + else + gAIScriptPtr += 8; +} + +static void BattleAICmd_if_can_faint(void) +{ + if (gBattleMoves[AI_THINKING_STRUCT->moveConsidered].power < 2) + { + gAIScriptPtr += 5; + return; + } + + gUnknown_02024400 = 0; + gUnknown_0202449C[0x13] = 0; + gUnknown_02024474[0xE] = 1; + gBattleMoveFlags = 0; + gCritMultiplier = 1; + gUnknown_020241EA = AI_THINKING_STRUCT->moveConsidered; + sub_8046E7C(gPlayerMonIndex, gEnemyMonIndex); + move_effectiveness_something(gUnknown_020241EA, gPlayerMonIndex, gEnemyMonIndex); + + gBattleMoveDamage = gBattleMoveDamage * AI_THINKING_STRUCT->unk18[AI_THINKING_STRUCT->movesetIndex] / 100; + + // moves always do at least 1 damage. + if (gBattleMoveDamage == 0) + gBattleMoveDamage = 1; + + if (gBattleMons[gEnemyMonIndex].hp <= gBattleMoveDamage) + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 1); + else + gAIScriptPtr += 5; +} + +static void BattleAICmd_if_cant_faint(void) +{ + if (gBattleMoves[AI_THINKING_STRUCT->moveConsidered].power < 2) + { + gAIScriptPtr += 5; + return; + } + + gUnknown_02024400 = 0; + gUnknown_0202449C[0x13] = 0; + gUnknown_02024474[0xE] = 1; + gBattleMoveFlags = 0; + gCritMultiplier = 1; + gUnknown_020241EA = AI_THINKING_STRUCT->moveConsidered; + sub_8046E7C(gPlayerMonIndex, gEnemyMonIndex); + move_effectiveness_something(gUnknown_020241EA, gPlayerMonIndex, gEnemyMonIndex); + + gBattleMoveDamage = gBattleMoveDamage * AI_THINKING_STRUCT->unk18[AI_THINKING_STRUCT->movesetIndex] / 100; + + // this macro is missing the damage 0 = 1 assumption. + + if (gBattleMons[gEnemyMonIndex].hp > gBattleMoveDamage) + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 1); + else + gAIScriptPtr += 5; +} + +static void BattleAICmd_if_has_move(void) +{ + int i; + u16 *temp_ptr = (u16 *)(gAIScriptPtr + 2); + + switch(gAIScriptPtr[1]) + { + case 1: + // _08132E42 + for (i = 0; i < 4; i++) + { + if (gBattleMons[gPlayerMonIndex].moves[i] == *temp_ptr) + break; + } + if (i == 4) + { + gAIScriptPtr += 8; + return; + } + else + { + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 4); + return; + } + case 3: // new to Emerald + if(gBattleMons[gPlayerMonIndex ^ 2].hp == 0) + { + gAIScriptPtr += 8; + return; + } + else + { + for (i = 0; i < 4; i++) + { + if (gBattleMons[gPlayerMonIndex ^ 2].moves[i] == *temp_ptr) + break; + } + } + if (i == 4) + { + gAIScriptPtr += 8; + return; + } + else + { + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 4); + return; + } + case 0: + case 2: + for (i = 0; i < 4; i++) + { + if (UNK_2016A00_STRUCT->unk0[gEnemyMonIndex][i] == *temp_ptr) + break; + } + if (i == 4) + { + gAIScriptPtr += 8; + return; + } + else + { + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 4); + return; + } + } +} + +static void BattleAICmd_if_dont_have_move(void) +{ + int i; + u16 *temp_ptr = (u16 *)(gAIScriptPtr + 2); + + switch(gAIScriptPtr[1]) + { + case 1: + case 3: // if_dont_have_move does not have the seperate 3 case check in Emerald unlike if_has_move. + for (i = 0; i < 4; i++) + { + if (gBattleMons[gPlayerMonIndex].moves[i] == *temp_ptr) + break; + } + if (i != 4) + { + gAIScriptPtr += 8; + return; + } + else + { + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 4); + return; + } + case 0: + case 2: + for (i = 0; i < 4; i++) + { + if (UNK_2016A00_STRUCT->unk0[gEnemyMonIndex][i] == *temp_ptr) + break; + } + if (i != 4) + { + gAIScriptPtr += 8; + return; + } + else + { + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 4); + return; + } + } +} + +static void BattleAICmd_if_move_effect(void) +{ + int i; + + switch (gAIScriptPtr[1]) + { + case 1: + case 3: // _08133044 + for(i = 0; i < 4; i++) + { + if(gBattleMons[gPlayerMonIndex].moves[i] != 0 && gBattleMoves[gBattleMons[gPlayerMonIndex].moves[i]].effect == gAIScriptPtr[2]) + break; + } + if (i == 4) + gAIScriptPtr += 7; + else + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 3); + break; + case 0: + case 2: // _08133090 + for (i = 0; i < 4; i++) + { + if (gBattleMons[gPlayerMonIndex].moves[i] != 0 && gBattleMoves[UNK_2016A00_STRUCT->unk0[gEnemyMonIndex][i]].effect == gAIScriptPtr[2]) + break; + } + if (i == 4) + gAIScriptPtr += 7; + else + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 3); + break; + } +} + +static void BattleAICmd_if_not_move_effect(void) +{ + int i; + + switch (gAIScriptPtr[1]) + { + case 1: + case 3: // _0813313C + for(i = 0; i < 4; i++) + { + if(gBattleMons[gPlayerMonIndex].moves[i] != 0 && gBattleMoves[gBattleMons[gPlayerMonIndex].moves[i]].effect == gAIScriptPtr[2]) + break; + } + if (i != 4) + gAIScriptPtr += 7; + else + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 3); + break; + case 0: + case 2: // _08133188 + for (i = 0; i < 4; i++) + { + if (UNK_2016A00_STRUCT->unk0[gEnemyMonIndex][i] && gBattleMoves[UNK_2016A00_STRUCT->unk0[gEnemyMonIndex][i]].effect == gAIScriptPtr[2]) + break; + } + if (i != 4) + gAIScriptPtr += 7; + else + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 3); + break; + } +} + +static void BattleAICmd_if_last_move_did_damage(void) +{ + u8 index; + + if (gAIScriptPtr[1] == USER) + index = gPlayerMonIndex; + else + index = gEnemyMonIndex; + + if (gAIScriptPtr[2] == 0) + { + if (gUnknown_020242BC[index].unk4 == 0) + { + gAIScriptPtr += 7; + return; + } + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 3); + return; + } + else if (gAIScriptPtr[2] != 1) // ignore the macro if its not 0 or 1. + { + gAIScriptPtr += 7; + return; + } + else if (gUnknown_020242BC[index].unk6 != 0) + { + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 3); + return; + } + gAIScriptPtr += 7; +} + +static void BattleAICmd_if_encored(void) +{ + switch (gAIScriptPtr[1]) + { + case 0: // _08109348 + if (gUnknown_020242BC[gUnknown_02024064].unk4 == AI_THINKING_STRUCT->moveConsidered) + { + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 2); + return; + } + gAIScriptPtr += 6; + return; + case 1: // _08109370 + if (gUnknown_020242BC[gUnknown_02024064].unk6 == AI_THINKING_STRUCT->moveConsidered) + { + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 2); + return; + } + gAIScriptPtr += 6; + return; + default: + gAIScriptPtr += 6; + return; + } +} + +static void BattleAICmd_flee(void) +{ + AI_THINKING_STRUCT->aiAction |= (AI_ACTION_UNK1 | AI_ACTION_UNK2 | AI_ACTION_UNK4); // what matters is UNK2 being enabled. +} + +static void BattleAICmd_if_random_100(void) +{ + u8 safariFleeRate = gUnknown_0202449C[0x7B] * 5; // safari flee rate, from 0-20 + + if ((u8)(Random() % 100) < safariFleeRate) + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 1); + else + gAIScriptPtr += 5; +} + +static void BattleAICmd_watch(void) +{ + AI_THINKING_STRUCT->aiAction |= (AI_ACTION_UNK1 | AI_ACTION_UNK3 | AI_ACTION_UNK4); // what matters is UNK3 being enabled. +} + +static void BattleAICmd_get_hold_effect(void) +{ + u8 index; + u16 status; + + if (gAIScriptPtr[1] == USER) + index = gPlayerMonIndex; + else + index = gEnemyMonIndex; + + if (gUnknown_02024064 != index) + { + AI_THINKING_STRUCT->funcResult = itemid_get_x12(UNK_2016A00_STRUCT->unk44[index]); + } + else + AI_THINKING_STRUCT->funcResult = itemid_get_x12(gBattleMons[index].item); + + gAIScriptPtr += 2; +} + +static void tai62_unk(void) +{ + u8 index = sub_8131E70(gAIScriptPtr[1]); + u16 item; + u8 var1, var2; + + if((index & 1) == (gPlayerMonIndex & 1)) + item = gBattleMons[index].item; + else + item = UNK_2016A00_STRUCT->unk44[index]; + + // strange way of loading a 16-bit argument from the AI command. + var2 = gAIScriptPtr[2]; + var1 = gAIScriptPtr[3]; + + if((var1 | var2) == item) + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 4); + else + gAIScriptPtr += 8; +} + +static void BattleAICmd_get_gender(void) +{ + u8 index; + + if (gAIScriptPtr[1] == USER) + index = gPlayerMonIndex; + else + index = gEnemyMonIndex; + + AI_THINKING_STRUCT->funcResult = pokemon_species_get_gender_info(gBattleMons[index].species, gBattleMons[index].personality); + + gAIScriptPtr += 2; +} + +static void BattleAICmd_is_first_turn(void) +{ + u8 index; + + if (gAIScriptPtr[1] == USER) + index = gPlayerMonIndex; + else + index = gEnemyMonIndex; + + AI_THINKING_STRUCT->funcResult = gUnknown_020242BC[index].unk16; + + gAIScriptPtr += 2; +} + +static void BattleAICmd_get_stockpile_count(void) +{ + u8 index; + + if (gAIScriptPtr[1] == USER) + index = gPlayerMonIndex; + else + index = gEnemyMonIndex; + + AI_THINKING_STRUCT->funcResult = gUnknown_020242BC[index].unk9; + + gAIScriptPtr += 2; +} + +static void BattleAICmd_is_double_battle(void) +{ + AI_THINKING_STRUCT->funcResult = gBattleTypeFlags & BATTLE_TYPE_DOUBLE; + + gAIScriptPtr += 1; +} + +static void BattleAICmd_get_item(void) +{ + u8 index; + + if (gAIScriptPtr[1] == USER) + index = gPlayerMonIndex; + else + index = gEnemyMonIndex; + + // this hack and a half matches. whatever. i dont care. someone else fix this mess later. PS: still cant fix this. + AI_THINKING_STRUCT->funcResult = gUnknown_0202449C[0xB8 + (index * 2)]; + + gAIScriptPtr += 2; +} + +static void BattleAICmd_get_move_type_from_result(void) +{ + AI_THINKING_STRUCT->funcResult = gBattleMoves[AI_THINKING_STRUCT->funcResult].type; + + gAIScriptPtr += 1; +} + +static void BattleAICmd_get_move_power_from_result(void) +{ + AI_THINKING_STRUCT->funcResult = gBattleMoves[AI_THINKING_STRUCT->funcResult].power; + + gAIScriptPtr += 1; +} + +static void BattleAICmd_get_move_effect_from_result(void) +{ + AI_THINKING_STRUCT->funcResult = gBattleMoves[AI_THINKING_STRUCT->funcResult].effect; + + gAIScriptPtr += 1; +} + +static void BattleAICmd_get_protect_count(void) +{ + u8 index; + + if (gAIScriptPtr[1] == USER) + index = gPlayerMonIndex; + else + index = gEnemyMonIndex; + + AI_THINKING_STRUCT->funcResult = gUnknown_020242BC[index].unk8; + + gAIScriptPtr += 2; +} + +static void BattleAICmd_nullsub_52(void) +{ +} + +static void BattleAICmd_nullsub_53(void) +{ +} + +static void BattleAICmd_nullsub_54(void) +{ +} + +static void BattleAICmd_nullsub_55(void) +{ +} + +static void BattleAICmd_nullsub_56(void) +{ +} + +static void BattleAICmd_nullsub_57(void) +{ +} + +static void BattleAICmd_call(void) +{ + b_mc_stack_push(gAIScriptPtr + 5); + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 1); +} + +static void BattleAICmd_jump(void) +{ + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 1); +} + +static void BattleAICmd_end(void) +{ + if (b_mc_stack_pop_cursor() == 0) + AI_THINKING_STRUCT->aiAction |= AI_ACTION_UNK1; +} + +static void BattleAICmd_if_level_cond(void) +{ + switch (gAIScriptPtr[1]) + { + case 0: // greater than + if (gBattleMons[gPlayerMonIndex].level > gBattleMons[gEnemyMonIndex].level) + { + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 2); + return; + } + gAIScriptPtr += 6; + return; + case 1: // less than + if (gBattleMons[gPlayerMonIndex].level < gBattleMons[gEnemyMonIndex].level) + { + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 2); + return; + } + gAIScriptPtr += 6; + return; + case 2: // equal + if (gBattleMons[gPlayerMonIndex].level == gBattleMons[gEnemyMonIndex].level) + { + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 2); + return; + } + gAIScriptPtr += 6; + return; + } +} + +static void BattleAICmd_if_taunted(void) +{ + if (gUnknown_020242BC[gEnemyMonIndex].taunt != 0) + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 1); + else + gAIScriptPtr += 5; +} + +static void BattleAICmd_if_not_taunted(void) +{ + if (gUnknown_020242BC[gEnemyMonIndex].taunt == 0) + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 1); + else + gAIScriptPtr += 5; +} + +static void tai5E_unk(void) +{ + if((gPlayerMonIndex & 1) == (gEnemyMonIndex & 1)) + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 1); + else + gAIScriptPtr += 5; +} + +static void tai61_unk(void) +{ + u8 index = sub_8131E70(gAIScriptPtr[1]); + + if(UNK_BATTLE_STRUCT->unk4->unkArray[index] & 1) + gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 2); + else + gAIScriptPtr += 6; +} + +void b_mc_stack_push(u8 *var) +{ + UNK_2016C00_STRUCT->ptr[UNK_2016C00_STRUCT->unk20++] = var; +} + +void b_mc_stack_push_cursor(void) +{ + UNK_2016C00_STRUCT->ptr[UNK_2016C00_STRUCT->unk20++] = gAIScriptPtr; +} + +bool8 b_mc_stack_pop_cursor(void) +{ + if (UNK_2016C00_STRUCT->unk20 != 0) + { + --UNK_2016C00_STRUCT->unk20; + gAIScriptPtr = UNK_2016C00_STRUCT->ptr[UNK_2016C00_STRUCT->unk20]; + return TRUE; + } + else + return FALSE; +} diff --git a/src/dma3_manager.c b/src/dma3_manager.c new file mode 100644 index 000000000..cd080a172 --- /dev/null +++ b/src/dma3_manager.c @@ -0,0 +1,528 @@ +#include "global.h" + +extern u8 gDma3ManagerLocked; +extern u8 gDma3RequestCursor; + +// size is 0x10 +struct DmaRequestsStruct +{ + /* 0x00 */ u8 *src; + /* 0x04 */ u8 *dest; + /* 0x08 */ u16 size; + /* 0x0A */ u16 mode; + /* 0x0C */ u32 value; +}; + +extern struct DmaRequestsStruct gDma3Requests[128]; + +void ClearDma3Requests(void) +{ + int i; + + gDma3ManagerLocked = TRUE; + gDma3RequestCursor = FALSE; + + for(i = 0; i < (u8)ARRAY_COUNT(gDma3Requests); i++) + { + gDma3Requests[i].size = 0; + gDma3Requests[i].src = 0; + gDma3Requests[i].dest = 0; + } + + gDma3ManagerLocked = FALSE; +} + +#ifdef NONMATCHING +void ProcessDma3Requests(void) +{ + // NOTE: the fillerA member of the DMA struct is actually u32 value; + // NOTE: gUnknown_0300001C is just a pointer inside the gDma3Requests structure, not a true symbol; feel free to remove + u16 total_size; + + if (gDma3ManagerLocked) + return; + + total_size = 0; + + // as long as there are DMA requests to process (unless size or vblank is an issue), do not exit + while (gDma3Requests[gDma3RequestCursor].size) + { + total_size += gDma3Requests[gDma3RequestCursor].size; + + if (total_size > 0xA000) + return; // don't do too much at once + + if (REG_VCOUNT > 224) + return;// we're about to leave vblank, stop + + switch (gDma3Requests[gDma3RequestCursor].mode) + { + case 1: // regular 32-bit copy + // _08000C8C + if(gDma3Requests[gDma3RequestCursor].size <= 0x1000) + { + DmaCopy32(3, gDma3Requests[gDma3RequestCursor].src, gDma3Requests[gDma3RequestCursor].dest, gDma3Requests[gDma3RequestCursor].size); + break; + } + while (gDma3Requests[gDma3RequestCursor].size > 0x1000) + { + DmaCopy32(3, gDma3Requests[gDma3RequestCursor].src, gDma3Requests[gDma3RequestCursor].dest, 0x1000); + gDma3Requests[gDma3RequestCursor].src += 0x1000; + gDma3Requests[gDma3RequestCursor].dest += 0x1000; + gDma3Requests[gDma3RequestCursor].size -= 0x1000; + } + DmaCopy32(3, gDma3Requests[gDma3RequestCursor].src, gDma3Requests[gDma3RequestCursor].dest, gDma3Requests[gDma3RequestCursor].size); + break; + case 2: // repeat a single 32-bit value across RAM + // _08000CD0 + while (gDma3Requests[gDma3RequestCursor].size > 0x1000) + { + DmaFill32(3, gDma3Requests[gDma3RequestCursor].value, gDma3Requests[gDma3RequestCursor].dest, 0x1000); + gDma3Requests[gDma3RequestCursor].dest += 0x1000; + gDma3Requests[gDma3RequestCursor].size -= 0x1000; + } + DmaFill32(3, gDma3Requests[gDma3RequestCursor].value, gDma3Requests[gDma3RequestCursor].dest, gDma3Requests[gDma3RequestCursor].size); + break; + case 3: // regular 16-bit copy + // _08000D3C + while (gDma3Requests[gDma3RequestCursor].size > 0x1000) + { + DmaCopy16(3, gDma3Requests[gDma3RequestCursor].src, gDma3Requests[gDma3RequestCursor].dest, 0x1000); + gDma3Requests[gDma3RequestCursor].src += 0x1000; + gDma3Requests[gDma3RequestCursor].dest += 0x1000; + gDma3Requests[gDma3RequestCursor].size -= 0x1000; + } + DmaCopy16(3, gDma3Requests[gDma3RequestCursor].src, gDma3Requests[gDma3RequestCursor].dest, gDma3Requests[gDma3RequestCursor].size); + break; + case 4: // repeat a single 16-bit value across RAM + // _08000D88 + while (gDma3Requests[gDma3RequestCursor].size > 0x1000) + { + DmaFill16(3, gDma3Requests[gDma3RequestCursor].value, gDma3Requests[gDma3RequestCursor].dest, 0x1000); + gDma3Requests[gDma3RequestCursor].dest += 0x1000; + gDma3Requests[gDma3RequestCursor].size -= 0x1000; + } + DmaFill16(3, gDma3Requests[gDma3RequestCursor].value, gDma3Requests[gDma3RequestCursor].dest, gDma3Requests[gDma3RequestCursor].size); + break; + } + gDma3Requests[gDma3RequestCursor].src = 0; + gDma3Requests[gDma3RequestCursor].dest = 0; + gDma3Requests[gDma3RequestCursor].size = 0; + gDma3Requests[gDma3RequestCursor].mode = 0; + gDma3Requests[gDma3RequestCursor].value = 0; + gDma3RequestCursor++; + + if (gDma3RequestCursor >= 128) // loop back to the first DMA request + gDma3RequestCursor = 0; + } +} +#else +__attribute__((naked)) +void ProcessDma3Requests(void) +{ + asm(".syntax unified\n\ + push {r4-r7,lr}\n\ + mov r7, r10\n\ + mov r6, r9\n\ + mov r5, r8\n\ + push {r5-r7}\n\ + sub sp, 0xC\n\ + ldr r0, =gDma3ManagerLocked\n\ + ldrb r0, [r0]\n\ + cmp r0, 0\n\ + beq _08000C06\n\ + b _08000E46\n\ +_08000C06:\n\ + movs r0, 0\n\ + str r0, [sp, 0x8]\n\ + ldr r1, =gDma3Requests\n\ + ldr r2, =gDma3RequestCursor\n\ + ldrb r0, [r2]\n\ + lsls r0, 4\n\ + adds r0, r1\n\ + ldrh r0, [r0, 0x8]\n\ + mov r12, r2\n\ + cmp r0, 0\n\ + bne _08000C1E\n\ + b _08000E46\n\ +_08000C1E:\n\ + mov r8, r1\n\ + adds r1, 0x4\n\ + mov r10, r1\n\ + movs r6, 0x80\n\ + lsls r6, 5\n\ + ldr r7, =0x040000D4 @REG_DMA3\n\ + movs r2, 0\n\ + mov r9, r2\n\ +_08000C2E:\n\ + mov r3, r12 @ gDma3RequestCursor\n\ + ldrb r0, [r3]\n\ + lsls r5, r0, 4\n\ + mov r0, r8 @ gDma3Requests\n\ + adds r1, r5, r0 @ gDma3Requests[gDma3RequestCursor]\n\ + ldrh r0, [r1, 0x8] @ gDma3Requests[gDma3RequestCursor].size\n\ + ldr r2, [sp, 0x8]\n\ + adds r0, r2, r0\n\ + lsls r0, 16\n\ + lsrs r0, 16\n\ + str r0, [sp, 0x8]\n\ + movs r0, 0xA0\n\ + lsls r0, 8\n\ + ldr r3, [sp, 0x8]\n\ + cmp r3, r0\n\ + bls _08000C50\n\ + b _08000E46\n\ +_08000C50:\n\ + ldr r0, =0x04000006 @REG_VCOUNT\n\ + ldrb r0, [r0]\n\ + cmp r0, 0xE0\n\ + bls _08000C5A\n\ + b _08000E46\n\ +_08000C5A:\n\ + ldrh r0, [r1, 0xA]\n\ + cmp r0, 0x2\n\ + beq _08000CD0\n\ + cmp r0, 0x2\n\ + bgt _08000C80\n\ + cmp r0, 0x1\n\ + beq _08000C8C\n\ + b _08000DF0\n\ + .pool\n\ +_08000C80:\n\ + cmp r0, 0x3\n\ + beq _08000D3C\n\ + cmp r0, 0x4\n\ + bne _08000C8A\n\ + b _08000D88\n\ +_08000C8A:\n\ + b _08000DF0\n\ +_08000C8C:\n\ + ldr r3, [r1]\n\ + mov r2, r10\n\ + adds r0, r5, r2\n\ + ldr r2, [r0]\n\ + ldrh r1, [r1, 0x8]\n\ + cmp r1, r6\n\ + bhi _08000CA6\n\ + str r3, [r7]\n\ + str r2, [r7, 0x4]\n\ + lsrs r0, r1, 2\n\ + movs r1, 0x84\n\ + lsls r1, 24\n\ + b _08000DAA\n\ +_08000CA6:\n\ + ldr r4, =0x040000D4 @REG_DMA3\n\ + str r3, [r4]\n\ + str r2, [r4, 0x4]\n\ + ldr r0, =0x84000400\n\ + str r0, [r4, 0x8]\n\ + ldr r0, [r4, 0x8]\n\ + adds r3, r6\n\ + adds r2, r6\n\ + subs r1, r6\n\ + cmp r1, r6\n\ + bhi _08000CA6\n\ + str r3, [r4]\n\ + str r2, [r4, 0x4]\n\ + lsrs r0, r1, 2\n\ + movs r1, 0x84\n\ + lsls r1, 24\n\ + b _08000D76\n\ + .pool\n\ +_08000CD0:\n\ + mov r3, r10\n\ + adds r0, r5, r3\n\ + ldr r4, [r0]\n\ + ldrh r1, [r1, 0x8]\n\ + cmp r1, r6\n\ + bhi _08000CF4\n\ + mov r0, r8\n\ + adds r0, 0xC\n\ + adds r0, r5, r0\n\ + ldr r0, [r0]\n\ + str r0, [sp]\n\ + mov r5, sp\n\ + str r5, [r7]\n\ + str r4, [r7, 0x4]\n\ + lsrs r0, r1, 2\n\ + movs r1, 0x85\n\ + lsls r1, 24\n\ + b _08000DAA\n\ +_08000CF4:\n\ + mov r2, r12\n\ + ldrb r0, [r2]\n\ + lsls r0, 4\n\ + mov r5, r8\n\ + adds r5, 0xC\n\ + adds r0, r5\n\ + ldr r0, [r0]\n\ + str r0, [sp]\n\ + ldr r3, =0x040000D4 @REG_DMA3\n\ + mov r0, sp\n\ + str r0, [r3]\n\ + str r4, [r3, 0x4]\n\ + ldr r0, =0x85000400\n\ + str r0, [r3, 0x8]\n\ + ldr r0, [r3, 0x8]\n\ + adds r4, r6\n\ + subs r1, r6\n\ + cmp r1, r6\n\ + bhi _08000CF4\n\ + ldrb r0, [r2]\n\ + lsls r0, 4\n\ + adds r0, r5\n\ + ldr r0, [r0]\n\ + str r0, [sp]\n\ + mov r2, sp\n\ + str r2, [r3]\n\ + str r4, [r3, 0x4]\n\ + lsrs r0, r1, 2\n\ + movs r1, 0x85\n\ + lsls r1, 24\n\ + b _08000DEA\n\ + .pool\n\ +_08000D3C:\n\ + ldr r3, [r1]\n\ + mov r2, r10\n\ + adds r0, r5, r2\n\ + ldr r2, [r0]\n\ + ldrh r1, [r1, 0x8]\n\ + cmp r1, r6\n\ + bhi _08000D56\n\ + str r3, [r7]\n\ + str r2, [r7, 0x4]\n\ + lsrs r0, r1, 1\n\ + movs r1, 0x80\n\ + lsls r1, 24\n\ + b _08000DAA\n\ +_08000D56:\n\ + ldr r4, =0x040000D4 @REG_DMA3\n\ + str r3, [r4]\n\ + str r2, [r4, 0x4]\n\ + ldr r0, =0x80000800\n\ + str r0, [r4, 0x8]\n\ + ldr r0, [r4, 0x8]\n\ + adds r3, r6\n\ + adds r2, r6\n\ + subs r1, r6\n\ + cmp r1, r6\n\ + bhi _08000D56\n\ + str r3, [r4]\n\ + str r2, [r4, 0x4]\n\ + lsrs r0, r1, 1\n\ + movs r1, 0x80\n\ + lsls r1, 24\n\ +_08000D76:\n\ + orrs r0, r1\n\ + str r0, [r4, 0x8]\n\ + ldr r0, [r4, 0x8]\n\ + b _08000DF0\n\ + .pool\n\ +_08000D88:\n\ + mov r3, r10\n\ + adds r0, r5, r3\n\ + ldr r2, [r0]\n\ + ldrh r4, [r1, 0x8]\n\ + add r1, sp, 0x4\n\ + cmp r4, r6\n\ + bhi _08000DB2\n\ + mov r0, r8\n\ + adds r0, 0xC\n\ + adds r0, r5, r0\n\ + ldr r0, [r0]\n\ + strh r0, [r1]\n\ + str r1, [r7]\n\ + str r2, [r7, 0x4]\n\ + lsrs r0, r4, 1\n\ + movs r1, 0x81\n\ + lsls r1, 24\n\ +_08000DAA:\n\ + orrs r0, r1\n\ + str r0, [r7, 0x8]\n\ + ldr r0, [r7, 0x8]\n\ + b _08000DF0\n\ +_08000DB2:\n\ + mov r5, r12\n\ + ldrb r0, [r5]\n\ + lsls r0, 4\n\ + ldr r3, =gUnknown_0300001C\n\ + adds r0, r3\n\ + ldr r0, [r0]\n\ + strh r0, [r1]\n\ + ldr r3, =0x040000D4 @REG_DMA3\n\ + str r1, [r3]\n\ + str r2, [r3, 0x4]\n\ + ldr r0, =0x81000800\n\ + str r0, [r3, 0x8]\n\ + ldr r0, [r3, 0x8]\n\ + adds r2, r6\n\ + subs r4, r6\n\ + cmp r4, r6\n\ + bhi _08000DB2\n\ + ldrb r0, [r5]\n\ + lsls r0, 4\n\ + ldr r5, =gUnknown_0300001C\n\ + adds r0, r5\n\ + ldr r0, [r0]\n\ + strh r0, [r1]\n\ + str r1, [r3]\n\ + str r2, [r3, 0x4]\n\ + lsrs r0, r4, 1\n\ + movs r1, 0x81\n\ + lsls r1, 24\n\ +_08000DEA:\n\ + orrs r0, r1\n\ + str r0, [r3, 0x8]\n\ + ldr r0, [r3, 0x8]\n\ +_08000DF0:\n\ + ldr r1, =gDma3Requests\n\ + mov r3, r12\n\ + ldrb r0, [r3]\n\ + lsls r0, 4\n\ + adds r0, r1\n\ + mov r2, r9\n\ + str r2, [r0]\n\ + ldrb r0, [r3]\n\ + lsls r0, 4\n\ + add r0, r10\n\ + str r2, [r0]\n\ + ldrb r0, [r3]\n\ + lsls r0, 4\n\ + adds r0, r1\n\ + movs r4, 0\n\ + strh r2, [r0, 0x8]\n\ + ldrb r0, [r3]\n\ + lsls r0, 4\n\ + adds r0, r1\n\ + mov r5, r9\n\ + strh r5, [r0, 0xA]\n\ + ldrb r0, [r3]\n\ + lsls r0, 4\n\ + adds r1, 0xC\n\ + adds r0, r1\n\ + mov r1, r9\n\ + str r1, [r0]\n\ + ldrb r0, [r3]\n\ + adds r0, 0x1\n\ + strb r0, [r3]\n\ + lsls r0, 24\n\ + cmp r0, 0\n\ + bge _08000E34\n\ + strb r4, [r3]\n\ +_08000E34:\n\ + mov r2, r12\n\ + ldrb r0, [r2]\n\ + lsls r0, 4\n\ + ldr r3, =gDma3Requests\n\ + adds r0, r3\n\ + ldrh r0, [r0, 0x8]\n\ + cmp r0, 0\n\ + beq _08000E46\n\ + b _08000C2E\n\ +_08000E46:\n\ + add sp, 0xC\n\ + pop {r3-r5}\n\ + mov r8, r3\n\ + mov r9, r4\n\ + mov r10, r5\n\ + pop {r4-r7}\n\ + pop {r0}\n\ + bx r0\n\ + .pool\n\ + .syntax divided"); +} +#endif + +int RequestDma3Copy(void *src, void *dest, u16 size, u8 mode) +{ + int cursor; + int var = 0; + + gDma3ManagerLocked = 1; + + cursor = gDma3RequestCursor; + while(1) + { + if(!gDma3Requests[cursor].size) // an empty copy was found and the current cursor will be returned. + { + gDma3Requests[cursor].src = src; + gDma3Requests[cursor].dest = dest; + gDma3Requests[cursor].size = size; + + if(mode == 1) + gDma3Requests[cursor].mode = mode; + else + gDma3Requests[cursor].mode = 3; + + gDma3ManagerLocked = FALSE; + return (s16)cursor; + } + if(++cursor >= 0x80) // loop back to start. + { + cursor = 0; + } + if(++var >= 0x80) // max checks were made. all resulted in failure. + { + break; + } + } + gDma3ManagerLocked = FALSE; + return -1; +} + +int RequestDma3Fill(s32 value, void *dest, u16 size, u8 mode) +{ + int cursor; + int var = 0; + + cursor = gDma3RequestCursor; + gDma3ManagerLocked = 1; + + while(1) + { + if(!gDma3Requests[cursor].size) + { + gDma3Requests[cursor].dest = dest; + gDma3Requests[cursor].size = size; + gDma3Requests[cursor].mode = mode; + gDma3Requests[cursor].value = value; + + if(mode == 1) + gDma3Requests[cursor].mode = 2; + else + gDma3Requests[cursor].mode = 4; + + gDma3ManagerLocked = FALSE; + return (s16)cursor; + } + if(++cursor >= 0x80) // loop back to start. + { + cursor = 0; + } + if(++var >= 0x80) // max checks were made. all resulted in failure. + { + break; + } + } + gDma3ManagerLocked = FALSE; + return -1; +} + +int CheckForSpaceForDma3Request(s16 index) +{ + int current = 0; + + if (index == -1) + { + for (; current < 0x80; current ++) + if (gDma3Requests[current].size) + return -1; + + return 0; + } + + if (gDma3Requests[index].size) + return -1; + + return 0; +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 000000000..a88c378eb --- /dev/null +++ b/src/main.c @@ -0,0 +1,418 @@ +#include "global.h" +#include "main.h" +#include "gba/flash_internal.h" +#include "gba/m4a_internal.h" + +extern u16 GetGpuReg(u8); +extern void SetGpuReg(u8, u16); +extern void LinkVSync(void); +extern void sub_800E174(void); +extern void sub_800B9B8(void); + +extern struct SoundInfo gSoundInfo; +extern u32 gFlashMemoryPresent; +extern u32 IntrMain[]; +extern u8 gHeap[]; +extern struct SaveBlock2 gUnknown_02024A54; +extern char *gUnknown_03005D94; +extern char gUnknown_02029808[]; +extern u32 gBattleTypeFlags; +extern u8 gUnknown_03002748; +extern u32 *gUnknown_0203CF5C; + +void Timer3Intr(void); +bool8 HandleLinkConnection(void); +void c2_copyright_1(void); + +static void VBlankIntr(void); +static void HBlankIntr(void); +static void VCountIntr(void); +static void SerialIntr(void); +static void IntrDummy(void); + +const u8 gGameVersion = VERSION_EMERALD; + +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))) + +extern u16 gUnknown_03000000; + +extern u16 gKeyRepeatStartDelay; +extern u8 gUnknown_030022B4; +extern struct Main gMain; +extern u16 gKeyRepeatContinueDelay; +extern u8 gSoftResetDisabled; +extern IntrFunc gIntrTable[INTR_COUNT]; +extern bool8 gLinkVSyncDisabled; +extern u32 IntrMain_Buffer[0x200]; +extern u8 gPcmDmaCounter; + +extern u16 gTrainerId; + +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); + +#define B_START_SELECT (B_BUTTON | START_BUTTON | SELECT_BUTTON) + +void AgbMain() +{ + RegisterRamReset(RESET_ALL); + *(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(); + ClearDma3Requests(); + ResetBgs(); + SetDefaultFontsPointer(); + InitHeap(gHeap, 0x1C000); + + gSoftResetDisabled = FALSE; + + if (gFlashMemoryPresent != TRUE) + SetMainCallback2(NULL); + + gUnknown_030022B4 = 0; + 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) + { + gUnknown_030022B4 = 1; + UpdateLinkAndCallCallbacks(); + gUnknown_030022B4 = 0; + } + else + { + gUnknown_030022B4 = 0; + UpdateLinkAndCallCallbacks(); + + if (sub_80875C8() == 1) + { + gMain.newKeys = 0; + ClearObjectCopyRequests(); + gUnknown_030022B4 = 1; + UpdateLinkAndCallCallbacks(); + gUnknown_030022B4 = 0; + } + } + + PlayTimeCounter_Update(); + MapMusicMain(); + WaitForVBlank(); + } +} + +static void UpdateLinkAndCallCallbacks(void) +{ + if (!HandleLinkConnection()) + CallCallbacks(); +} + +static void InitMainCallbacks(void) +{ + gMain.vblankCounter1 = 0; + gUnknown_0203CF5C = NULL; + gMain.vblankCounter2 = 0; + gMain.callback1 = NULL; + SetMainCallback2(c2_copyright_1); + gSaveBlock2Ptr = &gUnknown_02024A54; + gUnknown_03005D94 = gUnknown_02029808; +} + +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 GetTrainerId(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); +} + +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 == 2) + { + 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) +{ + 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(0x1); +} + +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; +} + +extern void CopyBufferedValuesToGpuRegs(void); +extern void ProcessDma3Requests(void); + +static void VBlankIntr(void) +{ + if (gLinkVSyncDisabled != FALSE) + LinkVSync(); + else if (gUnknown_03002748 == FALSE) + sub_800B9B8(); + + gMain.vblankCounter1++; + + if (gUnknown_0203CF5C && *gUnknown_0203CF5C < 0xFFFFFFFF) + (*gUnknown_0203CF5C)++; + + if (gMain.vblankCallback) + gMain.vblankCallback(); + + gMain.vblankCounter2++; + + CopyBufferedValuesToGpuRegs(); + ProcessDma3Requests(); + + gPcmDmaCounter = gSoundInfo.pcmDmaCounter; + + m4aSoundMain(); + sub_8033648(); + + if (!gMain.inBattle || (gBattleTypeFlags & 0x013F0102) == 0) + Random(); + + sub_800E174(); + + INTR_CHECK |= INTR_FLAG_VBLANK; + gMain.intrCheck |= INTR_FLAG_VBLANK; +} + +void StartFlashMemoryTimer(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 sub_80008DC(u32 *var) +{ + gUnknown_0203CF5C = var; +} + +void sub_80008E8(void) +{ + gUnknown_0203CF5C = NULL; +} + +void DoSoftReset(void) +{ + REG_IME = 0; + m4aSoundVSyncOff(); + remove_some_task(); + DmaStop(1); + DmaStop(2); + DmaStop(3); + SiiRtcProtect(); + SoftReset(RESET_ALL); +} + +void ClearPokemonCrySongs(void) +{ + CpuFill16(0, gPokemonCrySongs, MAX_POKEMON_CRIES * sizeof(struct PokemonCrySong)); +} diff --git a/src/multiboot.c b/src/multiboot.c new file mode 100644 index 000000000..80291ff46 --- /dev/null +++ b/src/multiboot.c @@ -0,0 +1,470 @@ +#include "gba/gba.h" +#include "multiboot.h" + +static u16 MultiBoot_required_data[MULTIBOOT_NCHILD]; + +static int MultiBootSend(struct MultiBootParam *mp, u16 data); +static int MultiBootHandShake(struct MultiBootParam *mp); +static void MultiBootWaitCycles(u32 cycles); +static void MultiBootWaitSendDone(void); + +void MultiBootInit(struct MultiBootParam *mp) +{ + mp->client_bit = 0; + mp->probe_count = 0; + mp->response_bit = 0; + + mp->check_wait = MULTIBOOT_CONNECTION_CHECK_WAIT; + mp->sendflag = 0; + + mp->handshake_timeout = 0; + + REG_RCNT = 0; + REG_SIOCNT = SIO_MULTI_MODE | SIO_115200_BPS; + REG_SIODATA8 = 0; +} + +int MultiBootMain(struct MultiBootParam *mp) +{ + int i; + int j; + int k; + + if (MultiBootCheckComplete(mp)) + { + return 0; + } + + if (mp->check_wait > MULTIBOOT_CONNECTION_CHECK_WAIT) + { + mp->check_wait--; + return 0; + } + +output_burst: + if (mp->sendflag) + { + mp->sendflag = 0; + + i = REG_SIOCNT & (SIO_MULTI_BUSY | SIO_ERROR | SIO_ID | SIO_MULTI_SD | SIO_MULTI_SI); + if (i != SIO_MULTI_SD) + { + MultiBootInit(mp); + return i ^ SIO_MULTI_SD; + } + } + + if (mp->probe_count >= 0xe0) + { + i = MultiBootHandShake(mp); + if (i) + { + return i; + } + + if (mp->server_type == MULTIBOOT_SERVER_TYPE_QUICK + && mp->probe_count > 0xe1 + && MultiBootCheckComplete(mp) == 0) + { + MultiBootWaitSendDone(); + goto output_burst; + } + + if (MultiBootCheckComplete(mp) == 0) + { + if (mp->handshake_timeout == 0) + { + MultiBootInit(mp); + return MULTIBOOT_ERROR_HANDSHAKE_FAILURE; + } + mp->handshake_timeout--; + } + + return 0; + } + + switch (mp->probe_count) + { + case 0: + k = 0x0e; + for (i = MULTIBOOT_NCHILD; i != 0; i--) + { + if (*(vu16 *)(REG_ADDR_SIOMULTI0 + i * 2) != 0xffff) + { + break; + } + k >>= 1; + } + + k &= 0x0e; + mp->response_bit = k; + + for (i = MULTIBOOT_NCHILD; i != 0; i--) + { + j = *(vu16 *)(REG_ADDR_SIOMULTI0 + i * 2); + if (mp->client_bit & (1 << i)) + { + if (j != ((MULTIBOOT_CLIENT_INFO << 8) | (1 << i))) + { + k = 0; + break; + } + } + } + + mp->client_bit &= k; + + if (k == 0) + { + mp->check_wait = MULTIBOOT_CONNECTION_CHECK_WAIT; + } + + if (mp->check_wait) + { + mp->check_wait--; + } + else + { + if (mp->response_bit != mp->client_bit) + { + MultiBootStartProbe(mp); + goto case_1; + } + } + + output_master_info: + return MultiBootSend(mp, (MULTIBOOT_MASTER_INFO << 8) | mp->client_bit); + + case_1: + case 1: + mp->probe_target_bit = 0; + for (i = MULTIBOOT_NCHILD; i != 0; i--) + { + j = *(vu16 *)(REG_ADDR_SIOMULTI0 + i * 2); + if ((j >> 8) == MULTIBOOT_CLIENT_INFO) + { + MultiBoot_required_data[i - 1] = j; + j &= 0xff; + if (j == (1 << i)) + { + mp->probe_target_bit |= j; + } + } + } + + if (mp->response_bit != mp->probe_target_bit) + { + goto output_master_info; + } + + mp->probe_count = 2; + return MultiBootSend(mp, (MULTIBOOT_MASTER_START_PROBE << 8) | mp->probe_target_bit); + + case 2: + for (i = MULTIBOOT_NCHILD; i != 0; i--) + { + if (mp->probe_target_bit & (1 << i)) + { + j = *(vu16 *)(REG_ADDR_SIOMULTI0 + i * 2); + if (j != MultiBoot_required_data[i - 1]) + { + mp->probe_target_bit ^= 1 << i; + } + } + } + goto output_header; + + case 0xd0: + k = 1; + for (i = MULTIBOOT_NCHILD; i != 0; i--) + { + j = *(vu16 *)(REG_ADDR_SIOMULTI0 + i * 2); + mp->client_data[i - 1] = j; + if (mp->probe_target_bit & (1 << i)) + { + if ((j >> 8) != MULTIBOOT_CLIENT_INFO + && (j >> 8) != MULTIBOOT_CLIENT_DLREADY) + { + MultiBootInit(mp); + return MULTIBOOT_ERROR_NO_DLREADY; + } + if (j == MultiBoot_required_data[i - 1]) + { + k = 0; + } + } + } + + if (k == 0) + { + return MultiBootSend(mp, (MULTIBOOT_MASTER_REQUEST_DLREADY << 8) | mp->palette_data); + } + + mp->probe_count = 0xd1; + + k = 0x11; + for (i = MULTIBOOT_NCHILD; i != 0; i--) + { + k += mp->client_data[i - 1]; + } + mp->handshake_data = k; + return MultiBootSend(mp, (MULTIBOOT_MASTER_START_DL << 8) | (k & 0xff)); + + case 0xd1: + for (i = MULTIBOOT_NCHILD; i != 0; i--) + { + j = *(vu16 *)(REG_ADDR_SIOMULTI0 + i * 2); + if (mp->probe_target_bit & (1 << i)) + { + if ((j >> 8) != MULTIBOOT_CLIENT_DLREADY) + { + MultiBootInit(mp); + return MULTIBOOT_ERROR_NO_DLREADY; + } + } + } + + i = MultiBoot(mp); + + if (i == 0) + { + mp->probe_count = 0xe0; + mp->handshake_timeout = MULTIBOOT_HANDSHAKE_TIMEOUT; + return 0; + } + MultiBootInit(mp); + mp->check_wait = MULTIBOOT_CONNECTION_CHECK_WAIT * 2; + return MULTIBOOT_ERROR_BOOT_FAILURE; + + default: + for (i = MULTIBOOT_NCHILD; i != 0; i--) + { + if (mp->probe_target_bit & (1 << i)) + { + j = *(vu16 *)(REG_ADDR_SIOMULTI0 + i * 2); + if ((j >> 8) != (MULTIBOOT_MASTER_START_PROBE + 1 - (mp->probe_count >> 1)) + || ((j & 0xff) != (1 << i))) + { + mp->probe_target_bit ^= 1 << i; + } + } + } + + if (mp->probe_count == 0xc4) + { + mp->client_bit = mp->probe_target_bit & 0x0e; + mp->probe_count = 0; + goto output_master_info; + } + + output_header: + if (mp->probe_target_bit == 0) + { + MultiBootInit(mp); + return MULTIBOOT_ERROR_NO_PROBE_TARGET; + } + + mp->probe_count += 2; + if (mp->probe_count == 0xc4) + { + goto output_master_info; + } + i = MultiBootSend(mp, + (mp->masterp[mp->probe_count - 4 + 1] << 8) + | mp->masterp[mp->probe_count - 4]); + + if (i) + { + return i; + } + if (mp->server_type == MULTIBOOT_SERVER_TYPE_QUICK) + { + MultiBootWaitSendDone(); + goto output_burst; + } + return 0; + } +} + +static int MultiBootSend(struct MultiBootParam *mp, u16 data) +{ + int i; + + i = REG_SIOCNT & (SIO_MULTI_BUSY | SIO_MULTI_SD | SIO_MULTI_SI); + if (i != SIO_MULTI_SD) + { + MultiBootInit(mp); + return i ^ SIO_MULTI_SD; + } + + REG_SIODATA8 = data; + REG_SIOCNT = SIO_MULTI_MODE | SIO_START | SIO_115200_BPS; + + mp->sendflag = 1; + return 0; +} + +void MultiBootStartProbe(struct MultiBootParam *mp) +{ + if (mp->probe_count != 0) + { + MultiBootInit(mp); + return; + } + mp->check_wait = 0; + mp->client_bit = 0; + mp->probe_count = 1; +} + +void MultiBootStartMaster(struct MultiBootParam *mp, u8 *srcp, int length, u8 palette_color, s8 palette_speed) +{ + int i = 0; + + if (mp->probe_count != 0 + || mp->client_bit == 0 + || mp->check_wait != 0) + { + MultiBootInit(mp); + return; + } + + mp->boot_srcp = srcp; + length = (length + 15) & ~15; + if (length < MULTIBOOT_SEND_SIZE_MIN || length > MULTIBOOT_SEND_SIZE_MAX) + { + MultiBootInit(mp); + return; + } + + mp->boot_endp = srcp + length; + + switch (palette_speed) + { + case -4: + case -3: + case -2: + case -1: + i = (palette_color << 3) | (3 - palette_speed); + break; + case 0: + i = 0x38 | palette_color; + break; + case 1: + case 2: + case 3: + case 4: + i = (palette_color << 3) | (palette_speed - 1); + break; + } + + mp->palette_data = ((i & 0x3f) << 1) | 0x81; + mp->probe_count = 0xd0; +} + +int MultiBootCheckComplete(struct MultiBootParam *mp) +{ + if (mp->probe_count == 0xe9) + { + return 1; + } + + return 0; +} + +static int MultiBootHandShake(struct MultiBootParam *mp) +{ + int i, j; + +#define send_data (mp->system_work[0]) +#define must_data (mp->system_work[1]) + + switch (mp->probe_count) + { + case_0xe0: + case 0xe0: + mp->probe_count = 0xe1; + must_data = 0x0000; + send_data = 0x100000; + return MultiBootSend(mp, 0x0000); + + default: + for (i = MULTIBOOT_NCHILD; i != 0; i--) + { + j = *(vu16 *)(REG_ADDR_SIOMULTI0 + i * 2); + if ((mp->client_bit & (1 << i)) + && j != must_data) + { + goto case_0xe0; + } + } + mp->probe_count++; + must_data = send_data & 0xffff; + if (send_data == 0x0000) + { + must_data = mp->masterp[0xac] | (mp->masterp[0xad] << 8); + send_data = must_data << 5; + } + send_data >>= 5; + output_common: + return MultiBootSend(mp, send_data); + + case 0xe7: + case 0xe8: + for (i = MULTIBOOT_NCHILD; i != 0; i--) + { + j = *(vu16 *)(REG_ADDR_SIOMULTI0 + i * 2); + if ((mp->client_bit & (1 << i)) && j != must_data) + { + MultiBootInit(mp); + return MULTIBOOT_ERROR_HANDSHAKE_FAILURE; + } + } + + mp->probe_count++; + if (mp->probe_count == 0xe9) + { + return 0; + } + + send_data = mp->masterp[0xae] | (mp->masterp[0xaf] << 8); + must_data = send_data; + goto output_common; + } + +#undef send_data +#undef must_data +} + +static void MultiBootWaitCycles(u32 cycles) +{ + asm("mov r2, pc"); + asm("lsr r2, #24"); + asm("mov r1, #12"); + asm("cmp r2, #0x02"); + asm("beq MultiBootWaitCyclesLoop"); + + asm("mov r1, #13"); + asm("cmp r2, #0x08"); + asm("beq MultiBootWaitCyclesLoop"); + + asm("mov r1, #4"); + + asm("MultiBootWaitCyclesLoop:"); + asm("sub r0, r1"); + asm("bgt MultiBootWaitCyclesLoop"); +} + +static void MultiBootWaitSendDone(void) +{ + int i; + + for (i = 0; i < 31069; i++) + { + if ((REG_SIOCNT & SIO_START) == 0) + { + break; + } + } + + MultiBootWaitCycles(600); +} diff --git a/src/rng.c b/src/rng.c new file mode 100644 index 000000000..6f4f2ce55 --- /dev/null +++ b/src/rng.c @@ -0,0 +1,35 @@ +#include "global.h" +#include "rng.h" + +// The number 1103515245 comes from the example implementation of rand and srand +// in the ISO C standard. + +extern u32 gRngValue; +extern u32 gRng2Value; + +EWRAM_DATA static u8 sUnknown = 0; +EWRAM_DATA static u32 sRandCount = 0; + +u16 Random() +{ + gRngValue = 1103515245 * gRngValue + 24691; + sRandCount++; + return gRngValue >> 16; +} + +void SeedRng(u16 seed) +{ + gRngValue = seed; + sUnknown = 0; +} + +void SeedRng2(u16 seed) +{ + gRng2Value = seed; +} + +u16 Random2(void) +{ + gRng2Value = 1103515245 * gRng2Value + 24691; + return gRng2Value >> 16; +} diff --git a/src/siirtc.c b/src/siirtc.c new file mode 100644 index 000000000..965a068f1 --- /dev/null +++ b/src/siirtc.c @@ -0,0 +1,432 @@ +// Ruby/Sapphire/Emerald cartridges contain a Seiko Instruments Inc. (SII) +// S-3511A real-time clock (RTC). This library ("SIIRTC_V001") is for +// communicating with the RTC. + +#include "gba/gba.h" +#include "siirtc.h" + +#define STATUS_INTFE 0x02 // frequency interrupt enable +#define STATUS_INTME 0x08 // per-minute interrupt enable +#define STATUS_INTAE 0x20 // alarm interrupt enable +#define STATUS_24HOUR 0x40 // 0: 12-hour mode, 1: 24-hour mode +#define STATUS_POWER 0x80 // power on or power failure occurred + +#define TEST_MODE 0x80 // flag in the "second" byte + +#define ALARM_AM 0x00 +#define ALARM_PM 0x80 + +#define OFFSET_YEAR offsetof(struct SiiRtcInfo, year) +#define OFFSET_MONTH offsetof(struct SiiRtcInfo, month) +#define OFFSET_DAY offsetof(struct SiiRtcInfo, day) +#define OFFSET_DAY_OF_WEEK offsetof(struct SiiRtcInfo, dayOfWeek) +#define OFFSET_HOUR offsetof(struct SiiRtcInfo, hour) +#define OFFSET_MINUTE offsetof(struct SiiRtcInfo, minute) +#define OFFSET_SECOND offsetof(struct SiiRtcInfo, second) +#define OFFSET_STATUS offsetof(struct SiiRtcInfo, status) +#define OFFSET_ALARM_HOUR offsetof(struct SiiRtcInfo, alarmHour) +#define OFFSET_ALARM_MINUTE offsetof(struct SiiRtcInfo, alarmMinute) + +#define INFO_BUF(info, index) (*((u8 *)(info) + (index))) + +#define DATETIME_BUF(info, index) INFO_BUF(info, OFFSET_YEAR + index) +#define DATETIME_BUF_LEN (OFFSET_SECOND - OFFSET_YEAR + 1) + +#define TIME_BUF(info, index) INFO_BUF(info, OFFSET_HOUR + index) +#define TIME_BUF_LEN (OFFSET_SECOND - OFFSET_HOUR + 1) + +#define WR 0 // command for writing data +#define RD 1 // command for reading data + +#define CMD(n) (0x60 | (n << 1)) + +#define CMD_RESET CMD(0) +#define CMD_STATUS CMD(1) +#define CMD_DATETIME CMD(2) +#define CMD_TIME CMD(3) +#define CMD_ALARM CMD(4) + +#define GPIO_PORT_DATA (*(vu16 *)0x80000C4) +#define GPIO_PORT_DIRECTION (*(vu16 *)0x80000C6) +#define GPIO_PORT_READ_ENABLE (*(vu16 *)0x80000C8) + +extern vu16 GPIOPortDirection; + +static u16 sDummy; // unused variable +static bool8 sLocked; + +static int WriteCommand(u8 value); +static int WriteData(u8 value); +static u8 ReadData(); +static void EnableGpioPortRead(); +static void DisableGpioPortRead(); + +static const char AgbLibRtcVersion[] = "SIIRTC_V001"; + +void SiiRtcUnprotect() +{ + EnableGpioPortRead(); + sLocked = FALSE; +} + +void SiiRtcProtect() +{ + DisableGpioPortRead(); + sLocked = TRUE; +} + +u8 SiiRtcProbe() +{ + u8 errorCode; + struct SiiRtcInfo rtc; + + if (!SiiRtcGetStatus(&rtc)) + return 0; + + errorCode = 0; + + if ((rtc.status & (SIIRTCINFO_POWER | SIIRTCINFO_24HOUR)) == SIIRTCINFO_POWER + || (rtc.status & (SIIRTCINFO_POWER | SIIRTCINFO_24HOUR)) == 0) + { + // The RTC is in 12-hour mode. Reset it and switch to 24-hour mode. + + // Note that the conditions are redundant and equivalent to simply + // "(rtc.status & SIIRTCINFO_24HOUR) == 0". It's possible that this + // was also intended to handle resetting the clock after power failure + // but a mistake was made. + + if (!SiiRtcReset()) + return 0; + + errorCode++; + } + + SiiRtcGetTime(&rtc); + + if (rtc.second & TEST_MODE) + { + // The RTC is in test mode. Reset it to leave test mode. + + if (!SiiRtcReset()) + return (errorCode << 4) & 0xF0; + + errorCode++; + } + + return (errorCode << 4) | 1; +} + +bool8 SiiRtcReset() +{ + u8 result; + struct SiiRtcInfo rtc; + + if (sLocked == TRUE) + return FALSE; + + sLocked = TRUE; + + GPIO_PORT_DATA = 1; + GPIO_PORT_DATA = 5; + + GPIO_PORT_DIRECTION = 7; + + WriteCommand(CMD_RESET | WR); + + GPIO_PORT_DATA = 1; + GPIO_PORT_DATA = 1; + + sLocked = FALSE; + + rtc.status = SIIRTCINFO_24HOUR; + + result = SiiRtcSetStatus(&rtc); + + return result; +} + +bool8 SiiRtcGetStatus(struct SiiRtcInfo *rtc) +{ + u8 statusData; + + if (sLocked == TRUE) + return FALSE; + + sLocked = TRUE; + + GPIO_PORT_DATA = 1; + GPIO_PORT_DATA = 5; + + GPIO_PORT_DIRECTION = 7; + + WriteCommand(CMD_STATUS | RD); + + GPIO_PORT_DIRECTION = 5; + + statusData = ReadData(); + + rtc->status = (statusData & (STATUS_POWER | STATUS_24HOUR)) + | ((statusData & STATUS_INTAE) >> 3) + | ((statusData & STATUS_INTME) >> 2) + | ((statusData & STATUS_INTFE) >> 1); + + GPIO_PORT_DATA = 1; + GPIO_PORT_DATA = 1; + + sLocked = FALSE; + + return TRUE; +} + +bool8 SiiRtcSetStatus(struct SiiRtcInfo *rtc) +{ + u8 statusData; + + if (sLocked == TRUE) + return FALSE; + + sLocked = TRUE; + + GPIO_PORT_DATA = 1; + GPIO_PORT_DATA = 5; + + statusData = STATUS_24HOUR + | ((rtc->status & SIIRTCINFO_INTAE) << 3) + | ((rtc->status & SIIRTCINFO_INTME) << 2) + | ((rtc->status & SIIRTCINFO_INTFE) << 1); + + GPIO_PORT_DIRECTION = 7; + + WriteCommand(CMD_STATUS | WR); + + WriteData(statusData); + + GPIO_PORT_DATA = 1; + GPIO_PORT_DATA = 1; + + sLocked = FALSE; + + return TRUE; +} + +bool8 SiiRtcGetDateTime(struct SiiRtcInfo *rtc) +{ + u8 i; + + if (sLocked == TRUE) + return FALSE; + + sLocked = TRUE; + + GPIO_PORT_DATA = 1; + GPIO_PORT_DATA = 5; + + GPIO_PORT_DIRECTION = 7; + + WriteCommand(CMD_DATETIME | RD); + + GPIO_PORT_DIRECTION = 5; + + for (i = 0; i < DATETIME_BUF_LEN; i++) + DATETIME_BUF(rtc, i) = ReadData(); + + INFO_BUF(rtc, OFFSET_HOUR) &= 0x7F; + + GPIO_PORT_DATA = 1; + GPIO_PORT_DATA = 1; + + sLocked = FALSE; + + return TRUE; +} + +bool8 SiiRtcSetDateTime(struct SiiRtcInfo *rtc) +{ + u8 i; + + if (sLocked == TRUE) + return FALSE; + + sLocked = TRUE; + + GPIO_PORT_DATA = 1; + GPIO_PORT_DATA = 5; + + GPIO_PORT_DIRECTION = 7; + + WriteCommand(CMD_DATETIME | WR); + + for (i = 0; i < DATETIME_BUF_LEN; i++) + WriteData(DATETIME_BUF(rtc, i)); + + GPIO_PORT_DATA = 1; + GPIO_PORT_DATA = 1; + + sLocked = FALSE; + + return TRUE; +} + +bool8 SiiRtcGetTime(struct SiiRtcInfo *rtc) +{ + u8 i; + + if (sLocked == TRUE) + return FALSE; + + sLocked = TRUE; + + GPIO_PORT_DATA = 1; + GPIO_PORT_DATA = 5; + + GPIO_PORT_DIRECTION = 7; + + WriteCommand(CMD_TIME | RD); + + GPIO_PORT_DIRECTION = 5; + + for (i = 0; i < TIME_BUF_LEN; i++) + TIME_BUF(rtc, i) = ReadData(); + + INFO_BUF(rtc, OFFSET_HOUR) &= 0x7F; + + GPIO_PORT_DATA = 1; + GPIO_PORT_DATA = 1; + + sLocked = FALSE; + + return TRUE; +} + +bool8 SiiRtcSetTime(struct SiiRtcInfo *rtc) +{ + u8 i; + + if (sLocked == TRUE) + return FALSE; + + sLocked = TRUE; + + GPIO_PORT_DATA = 1; + GPIO_PORT_DATA = 5; + + GPIO_PORT_DIRECTION = 7; + + WriteCommand(CMD_TIME | WR); + + for (i = 0; i < TIME_BUF_LEN; i++) + WriteData(TIME_BUF(rtc, i)); + + GPIO_PORT_DATA = 1; + GPIO_PORT_DATA = 1; + + sLocked = FALSE; + + return TRUE; +} + +bool8 SiiRtcSetAlarm(struct SiiRtcInfo *rtc) +{ + u8 i; + u8 alarmData[2]; + + if (sLocked == TRUE) + return FALSE; + + sLocked = TRUE; + + // Decode BCD. + alarmData[0] = (rtc->alarmHour & 0xF) + 10 * ((rtc->alarmHour >> 4) & 0xF); + + // The AM/PM flag must be set correctly even in 24-hour mode. + + if (alarmData[0] < 12) + alarmData[0] = rtc->alarmHour | ALARM_AM; + else + alarmData[0] = rtc->alarmHour | ALARM_PM; + + alarmData[1] = rtc->alarmMinute; + + GPIO_PORT_DATA = 1; + GPIO_PORT_DATA = 5; + + GPIOPortDirection = 7; // Why is this the only instance that uses a symbol? + + WriteCommand(CMD_ALARM | WR); + + for (i = 0; i < 2; i++) + WriteData(alarmData[i]); + + GPIO_PORT_DATA = 1; + GPIO_PORT_DATA = 1; + + sLocked = FALSE; + + return TRUE; +} + +static int WriteCommand(u8 value) +{ + u8 i; + u8 temp; + + for (i = 0; i < 8; i++) + { + temp = ((value >> (7 - i)) & 1); + GPIO_PORT_DATA = (temp << 1) | 4; + GPIO_PORT_DATA = (temp << 1) | 4; + GPIO_PORT_DATA = (temp << 1) | 4; + GPIO_PORT_DATA = (temp << 1) | 5; + } + + // control reaches end of non-void function +} + +static int WriteData(u8 value) +{ + u8 i; + u8 temp; + + for (i = 0; i < 8; i++) + { + temp = ((value >> i) & 1); + GPIO_PORT_DATA = (temp << 1) | 4; + GPIO_PORT_DATA = (temp << 1) | 4; + GPIO_PORT_DATA = (temp << 1) | 4; + GPIO_PORT_DATA = (temp << 1) | 5; + } + + // control reaches end of non-void function +} + +static u8 ReadData() +{ + u8 i; + u8 temp; + u8 value; + + for (i = 0; i < 8; i++) + { + GPIO_PORT_DATA = 4; + GPIO_PORT_DATA = 4; + GPIO_PORT_DATA = 4; + GPIO_PORT_DATA = 4; + GPIO_PORT_DATA = 4; + GPIO_PORT_DATA = 5; + + temp = ((GPIO_PORT_DATA & 2) >> 1); + value = (value >> 1) | (temp << 7); // UB: accessing uninitialized var + } + + return value; +} + +static void EnableGpioPortRead() +{ + GPIO_PORT_READ_ENABLE = 1; +} + +static void DisableGpioPortRead() +{ + GPIO_PORT_READ_ENABLE = 0; +} diff --git a/src/string_util.c b/src/string_util.c new file mode 100644 index 000000000..009072dca --- /dev/null +++ b/src/string_util.c @@ -0,0 +1,780 @@ +#include "global.h" +#include "string_util.h" +#include "text.h" + +EWRAM_DATA u8 gUnknownStringVar[16] = {0}; + +static const u8 sDigits[] = __("0123456789ABCDEF"); + +static const s32 sPowersOfTen[] = +{ + 1, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000, +}; + +extern u8 gExpandedPlaceholder_Empty[]; +extern u8 gExpandedPlaceholder_Kun[]; +extern u8 gExpandedPlaceholder_Chan[]; +extern u8 gExpandedPlaceholder_Sapphire[]; +extern u8 gExpandedPlaceholder_Ruby[]; +extern u8 gExpandedPlaceholder_Emerald[]; +extern u8 gExpandedPlaceholder_Aqua[]; +extern u8 gExpandedPlaceholder_Magma[]; +extern u8 gExpandedPlaceholder_Archie[]; +extern u8 gExpandedPlaceholder_Maxie[]; +extern u8 gExpandedPlaceholder_Kyogre[]; +extern u8 gExpandedPlaceholder_Groudon[]; +extern u8 gExpandedPlaceholder_Brendan[]; +extern u8 gExpandedPlaceholder_May[]; + +u8 *StringCopy10(u8 *dest, const u8 *src) +{ + u8 i; + u32 limit = 10; + + for (i = 0; i < limit; i++) + { + dest[i] = src[i]; + + if (dest[i] == EOS) + return &dest[i]; + } + + dest[i] = EOS; + return &dest[i]; +} + +u8 *StringGetEnd10(u8 *str) +{ + u8 i; + u32 limit = 10; + + for (i = 0; i < limit; i++) + if (str[i] == EOS) + return &str[i]; + + str[i] = EOS; + return &str[i]; +} + +u8 *StringCopy7(u8 *dest, const u8 *src) +{ + s32 i; + s32 limit = 7; + + for (i = 0; i < limit; i++) + { + dest[i] = src[i]; + + if (dest[i] == EOS) + return &dest[i]; + } + + dest[i] = EOS; + return &dest[i]; +} + +u8 *StringCopy(u8 *dest, const u8 *src) +{ + while (*src != EOS) + { + *dest = *src; + dest++; + src++; + } + + *dest = EOS; + return dest; +} + +u8 *StringAppend(u8 *dest, const u8 *src) +{ + while (*dest != EOS) + dest++; + + return StringCopy(dest, src); +} + +u8 *StringCopyN(u8 *dest, const u8 *src, u8 n) +{ + u16 i; + + for (i = 0; i < n; i++) + dest[i] = src[i]; + + return &dest[n]; +} + +u8 *StringAppendN(u8 *dest, const u8 *src, u8 n) +{ + while (*dest != EOS) + dest++; + + return StringCopyN(dest, src, n); +} + +u16 StringLength(const u8 *str) +{ + u16 length = 0; + + while (str[length] != EOS) + length++; + + return length; +} + +s32 StringCompare(const u8 *str1, const u8 *str2) +{ + while (*str1 == *str2) + { + if (*str1 == EOS) + return 0; + str1++; + str2++; + } + + return *str1 - *str2; +} + +s32 StringCompareN(const u8 *str1, const u8 *str2, u32 n) +{ + while (*str1 == *str2) + { + if (*str1 == EOS) + return 0; + str1++; + str2++; + if (--n == 0) + return 0; + } + + return *str1 - *str2; +} + +bool8 IsStringLengthAtLeast(const u8 *str, s32 n) +{ + u8 i; + + for (i = 0; i < n; i++) + if (str[i] && str[i] != EOS) + return TRUE; + + return FALSE; +} + +u8 *ConvertIntToDecimalStringN(u8 *dest, s32 value, enum StringConvertMode mode, u8 n) +{ + enum { WAITING_FOR_NONZERO_DIGIT, WRITING_DIGITS, WRITING_SPACES } state; + s32 powerOfTen; + s32 largestPowerOfTen = sPowersOfTen[n - 1]; + + state = WAITING_FOR_NONZERO_DIGIT; + + if (mode == STR_CONV_MODE_RIGHT_ALIGN) + state = WRITING_SPACES; + + if (mode == STR_CONV_MODE_LEADING_ZEROS) + state = WRITING_DIGITS; + + for (powerOfTen = largestPowerOfTen; powerOfTen > 0; powerOfTen /= 10) + { + u8 c; + u16 digit = value / powerOfTen; + s32 temp = value - (powerOfTen * digit); + + if (state == WRITING_DIGITS) + { + u8 *out = dest++; + + if (digit <= 9) + c = sDigits[digit]; + else + c = CHAR_QUESTION_MARK; + + *out = c; + } + else if (digit != 0 || powerOfTen == 1) + { + u8 *out; + state = WRITING_DIGITS; + out = dest++; + + if (digit <= 9) + c = sDigits[digit]; + else + c = CHAR_QUESTION_MARK; + + *out = c; + } + else if (state == WRITING_SPACES) + { + *dest++ = 0x77; + } + + value = temp; + } + + *dest = EOS; + return dest; +} + +u8 *ConvertUIntToDecimalStringN(u8 *dest, u32 value, enum StringConvertMode mode, u8 n) +{ + enum { WAITING_FOR_NONZERO_DIGIT, WRITING_DIGITS, WRITING_SPACES } state; + s32 powerOfTen; + s32 largestPowerOfTen = sPowersOfTen[n - 1]; + + state = WAITING_FOR_NONZERO_DIGIT; + + if (mode == STR_CONV_MODE_RIGHT_ALIGN) + state = WRITING_SPACES; + + if (mode == STR_CONV_MODE_LEADING_ZEROS) + state = WRITING_DIGITS; + + for (powerOfTen = largestPowerOfTen; powerOfTen > 0; powerOfTen /= 10) + { + u8 c; + u16 digit = value / powerOfTen; + u32 temp = value - (powerOfTen * digit); + + if (state == WRITING_DIGITS) + { + u8 *out = dest++; + + if (digit <= 9) + c = sDigits[digit]; + else + c = CHAR_QUESTION_MARK; + + *out = c; + } + else if (digit != 0 || powerOfTen == 1) + { + u8 *out; + state = WRITING_DIGITS; + out = dest++; + + if (digit <= 9) + c = sDigits[digit]; + else + c = CHAR_QUESTION_MARK; + + *out = c; + } + else if (state == WRITING_SPACES) + { + *dest++ = 0x77; + } + + value = temp; + } + + *dest = EOS; + return dest; +} + +u8 *ConvertIntToHexStringN(u8 *dest, s32 value, enum StringConvertMode mode, u8 n) +{ + enum { WAITING_FOR_NONZERO_DIGIT, WRITING_DIGITS, WRITING_SPACES } state; + u8 i; + s32 powerOfSixteen; + s32 largestPowerOfSixteen = 1; + + for (i = 1; i < n; i++) + largestPowerOfSixteen *= 16; + + state = WAITING_FOR_NONZERO_DIGIT; + + if (mode == STR_CONV_MODE_RIGHT_ALIGN) + state = WRITING_SPACES; + + if (mode == STR_CONV_MODE_LEADING_ZEROS) + state = WRITING_DIGITS; + + for (powerOfSixteen = largestPowerOfSixteen; powerOfSixteen > 0; powerOfSixteen /= 16) + { + u8 c; + u32 digit = value / powerOfSixteen; + s32 temp = value % powerOfSixteen; + + if (state == WRITING_DIGITS) + { + char *out = dest++; + + if (digit <= 0xF) + c = sDigits[digit]; + else + c = CHAR_QUESTION_MARK; + + *out = c; + } + else if (digit != 0 || powerOfSixteen == 1) + { + char *out; + state = WRITING_DIGITS; + out = dest++; + + if (digit <= 0xF) + c = sDigits[digit]; + else + c = CHAR_QUESTION_MARK; + + *out = c; + } + else if (state == WRITING_SPACES) + { + *dest++ = 0x77; + } + + value = temp; + } + + *dest = EOS; + return dest; +} + +u8 *StringExpandPlaceholders(u8 *dest, const u8 *src) +{ + for (;;) + { + u8 c = *src++; + u8 placeholderId; + u8 *expandedString; + + switch (c) + { + case PLACEHOLDER_BEGIN: + placeholderId = *src++; + expandedString = GetExpandedPlaceholder(placeholderId); + dest = StringExpandPlaceholders(dest, expandedString); + break; + case EXT_CTRL_CODE_BEGIN: + *dest++ = c; + c = *src++; + *dest++ = c; + + switch (c) + { + case 0x07: + case 0x09: + case 0x0F: + case 0x15: + case 0x16: + case 0x17: + case 0x18: + break; + case 0x04: + *dest++ = *src++; + case 0x0B: + *dest++ = *src++; + default: + *dest++ = *src++; + } + break; + case EOS: + *dest = EOS; + return dest; + case 0xFA: + case 0xFB: + case 0xFE: + default: + *dest++ = c; + } + } +} + +u8 *StringBraille(u8 *dest, const u8 *src) +{ + u8 setBrailleFont[] = { 0xFC, 0x06, 0x06, 0xFF }; + u8 gotoLine2[] = { 0xFE, 0xFC, 0x0E, 0x02, 0xFF }; + + dest = StringCopy(dest, setBrailleFont); + + for (;;) + { + u8 c = *src++; + + switch (c) + { + case EOS: + *dest = c; + return dest; + case 0xFE: + dest = StringCopy(dest, gotoLine2); + break; + default: + *dest++ = c; + *dest++ = c + 0x40; + break; + } + } +} + +static u8 *ExpandPlaceholder_UnknownStringVar(void) +{ + return gUnknownStringVar; +} + +static u8 *ExpandPlaceholder_PlayerName(void) +{ + return gSaveBlock2Ptr->playerName; +} + +static u8 *ExpandPlaceholder_StringVar1(void) +{ + return gStringVar1; +} + +static u8 *ExpandPlaceholder_StringVar2(void) +{ + return gStringVar2; +} + +static u8 *ExpandPlaceholder_StringVar3(void) +{ + return gStringVar3; +} + +static u8 *ExpandPlaceholder_KunChan(void) +{ + if (gSaveBlock2Ptr->playerGender == MALE) + return gExpandedPlaceholder_Kun; + else + return gExpandedPlaceholder_Chan; +} + +static u8 *ExpandPlaceholder_RivalName(void) +{ + if (gSaveBlock2Ptr->playerGender == MALE) + return gExpandedPlaceholder_May; + else + return gExpandedPlaceholder_Brendan; +} + +static u8 *ExpandPlaceholder_Version(void) +{ + return gExpandedPlaceholder_Emerald; +} + +static u8 *ExpandPlaceholder_Aqua(void) +{ + return gExpandedPlaceholder_Aqua; +} + +static u8 *ExpandPlaceholder_Magma(void) +{ + return gExpandedPlaceholder_Magma; +} + +static u8 *ExpandPlaceholder_Archie(void) +{ + return gExpandedPlaceholder_Archie; +} + +static u8 *ExpandPlaceholder_Maxie(void) +{ + return gExpandedPlaceholder_Maxie; +} + +static u8 *ExpandPlaceholder_Kyogre(void) +{ + return gExpandedPlaceholder_Kyogre; +} + +static u8 *ExpandPlaceholder_Groudon(void) +{ + return gExpandedPlaceholder_Groudon; +} + +u8 *GetExpandedPlaceholder(u32 id) +{ + typedef u8 *(*ExpandPlaceholderFunc)(void); + + static const ExpandPlaceholderFunc funcs[] = + { + ExpandPlaceholder_UnknownStringVar, + ExpandPlaceholder_PlayerName, + ExpandPlaceholder_StringVar1, + ExpandPlaceholder_StringVar2, + ExpandPlaceholder_StringVar3, + ExpandPlaceholder_KunChan, + ExpandPlaceholder_RivalName, + ExpandPlaceholder_Version, + ExpandPlaceholder_Aqua, + ExpandPlaceholder_Magma, + ExpandPlaceholder_Archie, + ExpandPlaceholder_Maxie, + ExpandPlaceholder_Kyogre, + ExpandPlaceholder_Groudon, + }; + + if (id >= ARRAY_COUNT(funcs)) + return gExpandedPlaceholder_Empty; + else + return funcs[id](); +} + +u8 *StringFill(u8 *dest, u8 c, u16 n) +{ + u16 i; + + for (i = 0; i < n; i++) + *dest++ = c; + + *dest = EOS; + return dest; +} + +u8 *StringCopyPadded(u8 *dest, const u8 *src, u8 c, u16 n) +{ + while (*src != EOS) + { + *dest++ = *src++; + + if (n) + n--; + } + + n--; + + while (n != (u16)-1) + { + *dest++ = c; + n--; + } + + *dest = EOS; + return dest; +} + +u8 *StringFillWithTerminator(u8 *dest, u16 n) +{ + return StringFill(dest, EOS, n); +} + +u8 *StringCopyN_Multibyte(u8 *dest, u8 *src, u32 n) +{ + u32 i; + + for (i = n - 1; i != (u32)-1; i--) + { + if (*src == EOS) + { + break; + } + else + { + *dest++ = *src++; + if (*(src - 1) == 0xF9) + *dest++ = *src++; + } + } + + *dest = EOS; + return dest; +} + +u32 StringLength_Multibyte(u8 *str) +{ + u32 length = 0; + + while (*str != EOS) + { + if (*str == 0xF9) + str++; + str++; + length++; + } + + return length; +} + +u8 *WriteColorChangeControlCode(u8 *dest, u32 colorType, u8 color) +{ + *dest = 0xFC; + dest++; + + switch (colorType) + { + case 0: + *dest = 1; + dest++; + break; + case 1: + *dest = 3; + dest++; + break; + case 2: + *dest = 2; + dest++; + break; + } + + *dest = color; + dest++; + *dest = EOS; + return dest; +} + +bool32 sub_8009228(u8 *str) +{ + while (*str != EOS) + { + if (*str <= 0xA0) + if (*str != 0) + return TRUE; + str++; + } + + return FALSE; +} + +bool32 sub_800924C(u8 *str, s32 n) +{ + s32 i; + + for (i = 0; *str != EOS && i < n; i++) + { + if (*str <= 0xA0) + if (*str != 0) + return TRUE; + str++; + } + + return FALSE; +} + +u8 GetExtCtrlCodeLength(u8 code) +{ + static const u8 lengths[] = + { + 1, + 2, + 2, + 2, + 4, + 2, + 2, + 1, + 2, + 1, + 1, + 3, + 2, + 2, + 2, + 1, + 3, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + }; + + u8 length = 0; + if (code < ARRAY_COUNT(lengths)) + length = lengths[code]; + return length; +} + +static const u8 *SkipExtCtrlCode(const u8 *s) +{ + while (*s == 0xFC) + { + s++; + s += GetExtCtrlCodeLength(*s); + } + + return s; +} + +s32 StringCompareWithoutExtCtrlCodes(const u8 *str1, const u8 *str2) +{ + s32 retVal = 0; + + while (1) + { + str1 = SkipExtCtrlCode(str1); + str2 = SkipExtCtrlCode(str2); + + if (*str1 > *str2) + break; + + if (*str1 < *str2) + { + retVal = -1; + if (*str2 == 0xFF) + retVal = 1; + } + + if (*str1 == 0xFF) + return retVal; + + str1++; + str2++; + } + + retVal = 1; + + if (*str1 == 0xFF) + retVal = -1; + + return retVal; +} + +void ConvertInternationalString(u8 *s, u8 language) +{ + if (language == LANGUAGE_JAPANESE) + { + u8 i; + + StripExtCtrlCodes(s); + i = StringLength(s); + s[i++] = 0xFC; + s[i++] = 22; + s[i++] = 0xFF; + + i--; + + while (i != (u8)-1) + { + s[i + 2] = s[i]; + i--; + } + + s[0] = 0xFC; + s[1] = 21; + } +} + +void StripExtCtrlCodes(u8 *str) +{ + u16 srcIndex = 0; + u16 destIndex = 0; + while (str[srcIndex] != 0xFF) + { + if (str[srcIndex] == 0xFC) + { + srcIndex++; + srcIndex += GetExtCtrlCodeLength(str[srcIndex]); + } + else + { + str[destIndex++] = str[srcIndex++]; + } + } + str[destIndex] = 0xFF; +} diff --git a/src/task.c b/src/task.c index f84c83e80..bf8abd794 100644 --- a/src/task.c +++ b/src/task.c @@ -49,7 +49,6 @@ u8 CreateTask(TaskFunc func, u8 priority) return 0; } -#ifdef NONMATCHING static void InsertTask(u8 newTaskId) { u8 taskId = FindFirstActiveTask(); @@ -62,7 +61,7 @@ static void InsertTask(u8 newTaskId) return; } - for (;;) + while (1) { if (gTasks[newTaskId].priority < gTasks[taskId].priority) { @@ -70,112 +69,22 @@ static void InsertTask(u8 newTaskId) // so we insert the new task before it. gTasks[newTaskId].prev = gTasks[taskId].prev; gTasks[newTaskId].next = taskId; - if (gTasks[taskId].prev != HEAD_SENTINEL) gTasks[gTasks[taskId].prev].next = newTaskId; - gTasks[taskId].prev = newTaskId; return; } - - if (gTasks[taskId].next != TAIL_SENTINEL) - taskId = gTasks[taskId].next; - else - break; + if (gTasks[taskId].next == TAIL_SENTINEL) + { + // We've reached the end. + gTasks[newTaskId].prev = taskId; + gTasks[newTaskId].next = gTasks[taskId].next; + gTasks[taskId].next = newTaskId; + return; + } + taskId = gTasks[taskId].next; } - - // We've reached the end. - gTasks[newTaskId].prev = taskId; - gTasks[newTaskId].next = gTasks[taskId].next; - gTasks[taskId].next = newTaskId; -} -#else -__attribute__((naked)) -static void InsertTask(u8 newTaskId) -{ - asm("push {r4, r5, r6, r7, lr}\n\ - mov r7, r8\n\ - push {r7}\n\ - lsl r0, r0, #24\n\ - lsr r4, r0, #24\n\ - bl FindFirstActiveTask\n\ - lsl r0, r0, #24\n\ - lsr r1, r0, #24\n\ - cmp r1, #16\n\ - bne .LInsertTask_foundActiveTask\n\ - ldr r1, .LInsertTask_gTasks1\n\ - lsl r0, r4, #2\n\ - add r0, r0, r4\n\ - lsl r0, r0, #3\n\ - add r0, r0, r1\n\ - mov r1, #254\n\ - strb r1, [r0, #5]\n\ - mov r1, #255\n\ - strb r1, [r0, #6]\n\ - b .LInsertTask_done\n\ - .align 2, 0\n\ -.LInsertTask_gTasks1:\n\ - .word gTasks\n\ -.LInsertTask_foundActiveTask:\n\ - ldr r6, .LInsertTask_gTasks2\n\ - lsl r0, r4, #2\n\ - mov r12, r0\n\ - mov r8, r6\n\ - add r0, r0, r4\n\ - lsl r0, r0, #3\n\ - add r2, r0, r6\n\ -.LInsertTask_loop:\n\ - lsl r0, r1, #2\n\ - add r0, r0, r1\n\ - lsl r5, r0, #3\n\ - mov r7, r8\n\ - add r3, r5, r7\n\ - ldrb r0, [r2, #7]\n\ - ldrb r7, [r3, #7]\n\ - cmp r0, r7\n\ - bcs .LInsertTask_next\n\ - ldrb r0, [r3, #5]\n\ - strb r0, [r2, #5]\n\ - strb r1, [r2, #6]\n\ - ldrb r0, [r3, #5]\n\ - cmp r0, #254\n\ - beq .LInsertTask_insertAtHead\n\ - add r1, r0, #0\n\ - lsl r0, r1, #2\n\ - add r0, r0, r1\n\ - lsl r0, r0, #3\n\ - add r0, r0, r8\n\ - strb r4, [r0, #6]\n\ -.LInsertTask_insertAtHead:\n\ - strb r4, [r3, #5]\n\ - b .LInsertTask_done\n\ - .align 2, 0\n\ -.LInsertTask_gTasks2:\n\ - .word gTasks\n\ -.LInsertTask_next:\n\ - ldrb r0, [r3, #6]\n\ - cmp r0, #255\n\ - beq .LInsertTask_insertAtTail\n\ - add r1, r0, #0\n\ - b .LInsertTask_loop\n\ -.LInsertTask_insertAtTail:\n\ - mov r2, r12\n\ - add r0, r2, r4\n\ - lsl r0, r0, #3\n\ - add r0, r0, r6\n\ - strb r1, [r0, #5]\n\ - add r2, r5, r6\n\ - ldrb r1, [r2, #6]\n\ - strb r1, [r0, #6]\n\ - strb r4, [r2, #6]\n\ -.LInsertTask_done:\n\ - pop {r3}\n\ - mov r8, r3\n\ - pop {r4, r5, r6, r7}\n\ - pop {r0}\n\ - bx r0\n"); } -#endif // NONMATCHING void DestroyTask(u8 taskId) { |