mirror of
https://github.com/Ninjdai1/pokeemerald.git
synced 2024-12-26 19:54:21 +01:00
471 lines
11 KiB
C
471 lines
11 KiB
C
#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, const 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);
|
|
}
|