pokeemerald/src/multiboot.c
2018-11-19 17:16:01 +01:00

471 lines
11 KiB
C

#include "gba/gba.h"
#include "multiboot.h"
IWRAM_DATA 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);
}