Merge remote-tracking branch 'refs/remotes/pret/master'
This commit is contained in:
2838
src/battle_ai.c
Normal file
2838
src/battle_ai.c
Normal file
File diff suppressed because it is too large
Load Diff
528
src/dma3_manager.c
Normal file
528
src/dma3_manager.c
Normal file
@@ -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;
|
||||
}
|
||||
418
src/main.c
Normal file
418
src/main.c
Normal file
@@ -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));
|
||||
}
|
||||
470
src/multiboot.c
Normal file
470
src/multiboot.c
Normal file
@@ -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);
|
||||
}
|
||||
35
src/rng.c
Normal file
35
src/rng.c
Normal file
@@ -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;
|
||||
}
|
||||
432
src/siirtc.c
Normal file
432
src/siirtc.c
Normal file
@@ -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;
|
||||
}
|
||||
780
src/string_util.c
Normal file
780
src/string_util.c
Normal file
@@ -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;
|
||||
}
|
||||
111
src/task.c
111
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)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user