From 5ff38da9d4c1047d711fafb4451f2717a9abf6e9 Mon Sep 17 00:00:00 2001 From: Cuyler36 Date: Thu, 1 Jun 2023 17:20:41 -0400 Subject: [PATCH] Implement & link JoyBoot.c --- common.py | 8 +- config/disasm_overrides.yml | 2 + config/dol_slices.yml | 4 + configure.py | 4 + include/GBA/GBAPriv.h | 80 +++ include/GBA/gba.h | 65 +++ include/GBA2/gba2.h | 126 +++++ include/dolphin/dsp.h | 70 +++ include/dolphin/os/OSReset.h | 13 + include/dolphin/os/OSSerial.h | 70 +++ include/dolphin/sipriv.h | 56 ++ include/gamealloc.h | 3 + src/GBA/GBA.c | 93 ++++ src/GBA/GBAGetProcessStatus.c | 39 ++ src/GBA/GBAJoyBoot.c | 375 +++++++++++++ src/GBA/GBAKey.c | 114 ++++ src/GBA/GBARead.c | 40 ++ src/GBA/GBAWrite.c | 37 ++ src/GBA/GBAXfer.c | 124 +++++ src/GBA2/JoyBoot.c | 955 ++++++++++++++++++++++++++++++++++ 20 files changed, 2277 insertions(+), 1 deletion(-) create mode 100644 include/GBA/GBAPriv.h create mode 100644 include/GBA/gba.h create mode 100644 include/GBA2/gba2.h create mode 100644 include/dolphin/dsp.h create mode 100644 include/dolphin/os/OSSerial.h create mode 100644 include/dolphin/sipriv.h create mode 100644 src/GBA/GBA.c create mode 100644 src/GBA/GBAGetProcessStatus.c create mode 100644 src/GBA/GBAJoyBoot.c create mode 100644 src/GBA/GBAKey.c create mode 100644 src/GBA/GBARead.c create mode 100644 src/GBA/GBAWrite.c create mode 100644 src/GBA/GBAXfer.c create mode 100644 src/GBA2/JoyBoot.c diff --git a/common.py b/common.py index de18830a..8e2cfe8f 100644 --- a/common.py +++ b/common.py @@ -341,6 +341,11 @@ BOOT_CFLAGS = CFLAGS + [ "-sdata 0", "-sdata2 0" ] + DOL_DEFINES +DOL_CFLAGS_NO_SDATA = CFLAGS + [ + "-inline on", + "-sdata 0", + "-sdata2 0" +] + DOL_DEFINES DVDERR_CFLAGS = CFLAGS + [ "-inline on", "-sdata 0", @@ -395,8 +400,9 @@ JSYSTEM_BASE = [ JSYSTEM_CFLAGS = ' '.join(JSYSTEM_BASE + LOCAL_CFLAGS) DOL_CFLAGS = ' '.join(BASE_DOL_CFLAGS + LOCAL_CFLAGS) -DOL_BOOT_CFLAGS = ' '.join(BOOT_CFLAGS + LOCAL_CFLAGS) # TODO: this uses C++ but there's some issues with ppcdis, real flags: ' '.join(CPLFLAGS + BOOT_CFLAGS + LOCAL_CFLAGS); +DOL_BOOT_CFLAGS = ' '.join(BOOT_CFLAGS + LOCAL_CFLAGS) DOL_DVDERR_CFLAGS = ' '.join(DVDERR_CFLAGS + LOCAL_CFLAGS) +DOL_CFLAGS_SDATA0_CFLAGS = ' '.join(DOL_CFLAGS_NO_SDATA + LOCAL_CFLAGS) SDK_FLAGS = ' '.join(SDK_CFLAG + LOCAL_CFLAGS) ALIGN16 = ' '.join(BASE_DOL_CFLAGS + LOCAL_CFLAGS + ALIGN16_CFLAG) DOL_CPPFLAGS = ' '.join(CPLFLAGS + BASE_DOL_CFLAGS + LOCAL_CFLAGS) diff --git a/config/disasm_overrides.yml b/config/disasm_overrides.yml index 976ca27e..28c9c2ed 100644 --- a/config/disasm_overrides.yml +++ b/config/disasm_overrides.yml @@ -3,3 +3,5 @@ trim_ctors: true symbol_aligns: 0x80207458: 8 # align RunQueue to 0x001251d8 0x800b9140: 32 # align gam_win_moji1_tex to 32 bytes + 0x801f71c0: 32 # align texture_buffer_data to 32 bytes + 0x800daaa0: 32 # align texture_cache_data_func to 32 bytes \ No newline at end of file diff --git a/config/dol_slices.yml b/config/dol_slices.yml index 0cb6fec3..a57c0dca 100644 --- a/config/dol_slices.yml +++ b/config/dol_slices.yml @@ -41,6 +41,10 @@ jaudio_NES/dummyprobe.c: #jaudio_NES/verysimple.c: # .text: [0x80008400, 0x80008480] # .sdata: [0x80217b80, 0x80217b88] +GBA2/JoyBoot.c: + .text: [0x80049788, 0x8004ae00] + .data: [0x800daa08, 0x800daaa0] + .bss: [0x801f7180, 0x801f71c0] libforest/osreport.c: .text: [0x8005a654, 0x8005a92c] .data: [0x800dc6d8, 0x800dc738] diff --git a/configure.py b/configure.py index 56a38238..7fce39e4 100644 --- a/configure.py +++ b/configure.py @@ -631,6 +631,10 @@ class CSource(Source): self.cflags = c.JSYSTEM_CFLAGS self.cc = c.CC self.frank = False + elif path.startswith("src/GBA2/"): + self.cflags = c.DOL_CFLAGS_SDATA0_CFLAGS + self.cc = c.CC + self.frank = False elif path.startswith("src/bootdata/") or path == "src/boot.c" or path == "src/initial_menu.c": self.cflags = c.DOL_BOOT_CFLAGS self.cc = c.CC diff --git a/include/GBA/GBAPriv.h b/include/GBA/GBAPriv.h new file mode 100644 index 00000000..0ab7aa03 --- /dev/null +++ b/include/GBA/GBAPriv.h @@ -0,0 +1,80 @@ +#ifndef GBAPRIV +#define GBAPRIV + +#include "types.h" + +#include "GBA/gba.h" +#include "dolphin/os.h" +#include "dolphin/dsp.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*GBATransferCallback)(s32 chan); + +typedef struct GBASecParam { + u8 readbuf[4]; + s32 paletteColor; + s32 paletteSpeed; + s32 length; + u32* out; + u8 _padding0[12]; + u32 keyA; + s32 keyB; + u8 _padding1[24]; +} GBASecParam; + +typedef struct GBABootInfo { + s32 paletteColor; + s32 paletteSpeed; + u8* programp; + s32 length; + u8* status; + GBACallback callback; + u8 readbuf[4]; + u8 writebuf[4]; + int i; + OSTick start; + OSTime begin; + int firstXfer; + int curOffset; + u32 crc; + u32 dummyWord[7]; + u32 keyA; + s32 keyB; + u32 initialCode; + int realLength; +} GBABootInfo; + +typedef struct GBAControl { + u8 output[5]; + u8 input[5]; + s32 outputBytes; + s32 inputBytes; + u8* status; + u8* ptr; + GBACallback callback; + s32 ret; + OSThreadQueue threadQueue; + OSTime delay; + GBATransferCallback proc; + GBABootInfo bootInfo; + DSPTaskInfo task; + GBASecParam* param; +} GBAControl; + +extern GBAControl __GBA[4]; +extern BOOL __GBAReset; + +void __GBAHandler(s32 chan, u32 error, OSContext* context); +void __GBASyncCallback(s32 chan, s32 ret); +s32 __GBASync(s32 chan); +OSTime __GBASetDelay(s32 chan, OSTime delay); +s32 __GBATransfer(s32 chan, s32 w1, s32 w2, GBATransferCallback callback); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/GBA/gba.h b/include/GBA/gba.h new file mode 100644 index 00000000..edb915a7 --- /dev/null +++ b/include/GBA/gba.h @@ -0,0 +1,65 @@ +#ifndef GBA +#define GBA + +#include "types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define GBA_CHAN0 0 +#define GBA_CHAN1 1 +#define GBA_CHAN2 2 +#define GBA_CHAN3 3 +#define GBA_MAX_CHAN 4 + +#define GBA_ALL_KEY_MASK 0x03ff +#define GBA_A_BUTTON 0x0001 +#define GBA_B_BUTTON 0x0002 +#define GBA_SELECT_BUTTON 0x0004 +#define GBA_START_BUTTON 0x0008 +#define GBA_R_KEY 0x0010 +#define GBA_L_KEY 0x0020 +#define GBA_U_KEY 0x0040 +#define GBA_D_KEY 0x0080 +#define GBA_R_BUTTON 0x0100 +#define GBA_L_BUTTON 0x0200 + +#define GBA_JSTAT_MASK 0x3a +#define GBA_JSTAT_FLAGS_SHIFT 4 +#define GBA_JSTAT_FLAGS_MASK 0x30 +#define GBA_JSTAT_PSF1 0x20 +#define GBA_JSTAT_PSF0 0x10 +#define GBA_JSTAT_SEND 0x08 +#define GBA_JSTAT_RECV 0x02 + +#define GBA_READY 0 +#define GBA_NOT_READY 1 +#define GBA_BUSY 2 +#define GBA_JOYBOOT_UNKNOWN_STATE 3 +#define GBA_JOYBOOT_ERR_INVALID 4 + +#define GBA_JOYBOOT_PROGRAM_SIZE_MAX 0x40000 + +#define GBA_JOYBOOT_BOOTPARAM_OFFSET 0xc8 +#define GBA_JOYBOOT_BOOTPARAM_SIZE 0x18 +typedef void (*GBACallback)(s32 chan, s32 ret); +void GBAInit(void); +s32 GBAGetStatus(s32 chan, u8* status); +s32 GBAGetStatusAsync(s32 chan, u8* status, GBACallback callback); +s32 GBAReset(s32 chan, u8* status); +s32 GBAResetAsync(s32 chan, u8* status, GBACallback callback); +s32 GBAGetProcessStatus(s32 chan, u8* percentp); +s32 GBARead(s32 chan, u8* dst, u8* status); +s32 GBAReadAsync(s32 chan, u8* dst, u8* status, GBACallback callback); +s32 GBAWrite(s32 chan, u8* src, u8* status); +s32 GBAWriteAsync(s32 chan, u8* src, u8* status, GBACallback callback); +s32 GBAJoyBoot(s32 chan, s32 palette_color, s32 palette_speed, u8* programp, s32 length, + u8* status); +s32 GBAJoyBootAsync(s32 chan, s32 palette_color, s32 palette_speed, u8* programp, s32 length, + u8* status, GBACallback callback); +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/GBA2/gba2.h b/include/GBA2/gba2.h new file mode 100644 index 00000000..7e41c48d --- /dev/null +++ b/include/GBA2/gba2.h @@ -0,0 +1,126 @@ +#ifndef GBA2_H +#define GBA2_H + +#include "types.h" +#include "GBA/gba.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define EAPPLI_LOAD_PROG_MAGIC 'PSAE' +#define EAPPLI_PROGRAM_CHECK_MAGIC_GC 'GAFJ' +#define EAPPLI_PROGRAM_CHECK_MAGIC_GBA 'AAFJ' + +#define GBA2_GBA_STATE_ERROR -1 +#define GBA2_GBA_STATE_ALREADY_EXIST 0 +#define GBA2_GBA_STATE_SUCCESS 1 +#define GBA2_GBA_STATE_TRANSMITTING 9 + +#define GBA2_EAPPLI_TRANSMITTING -1 +#define GBA2_EAPPLI_SUCCESS 0 +#define GBA2_EAPPLI_FAILURE_XFER_ERROR 1 +#define GBA2_EAPPLI_FAILURE_NO_GBA 2 + +#define GBA2_SEND_RECV_TRIES 7 +#define GBA2_CONNECTED_TRIES 45 +#define GBA2_DEFAULT_TRIES 120 +#define GBA2_READ_TRIES 1200 + +enum eappli_type { + GBA2_EAPPLI_TYPE_NEEDLEWORK, /* Able Sisters' design program */ + GBA2_EAPPLI_TYPE_MUSICSCORE, /* Town tune editor program */ + GBA2_EAPPLI_TYPE_PTERMINAL, /* e-Card scanning program */ + + GBA2_EAPPLI_TYPE_NUM +}; + +enum send_eappli_state { + SEND_EAPPLI_STATE_WAIT_FOR_GBA, /* GC awaits load magic (PSAE) from GBA */ + SEND_EAPPLI_STATE_GC_ACKNOWLEDGE_TO_GBA, /* GC sends load magic (PSAE) back to GBA to acknowledge it is there */ + SEND_EAPPLI_STATE_2, /* skipped/unused state */ + SEND_EAPPLI_STATE_GC_SEND_EAPPLI_SIZE_TO_GBA, /* GC sends eAppli program data size to GBA */ + SEND_EAPPLI_STATE_WAIT_FOR_GBA_ACKNOWLEDGED_EAPPLI_SIZE, /* GC awaits GBA is ready for eAppli data */ + SEND_EAPPLI_STATE_GC_SEND_EAPPLI_DATA_TO_GBA, /* GC sends eAppli data to GBA */ + SEND_EAPPLI_STATE_GC_OPEN_JOY_SIO_CONNECTION, /* GC attempts to open connection with GBA eAppli program */ + SEND_EAPPLI_STATE_JOY_SIO_CONNECTION_OPEN /* GC <-> GBA connection is open */ +}; + +enum send_eappli_status { + SEND_EAPPLI_STATUS_SUCCESS, /* Program successfully sent & loaded */ + SEND_EAPPLI_STATUS_FAILED_GBA_NOT_DETECTED, /* GBA was not detected during first stage */ + SEND_EAPPLI_STATUS_FAILED_TRANSMISSION_ERROR, /* Transmission error occurred */ + SEND_EAPPLI_STATUS_SENDING /* Transmission is in progress */ +}; + +#define GBA2_MAKE_ACKNOWLEDGE_CODE(dev, state) (0xFFFE0000 | ((dev) << 8) | (state)) + +enum gba_gc_device { + GBA2_DEVICE_NONE, + GBA2_DEVICE_GC, + GBA2_DEVICE_GBA +}; + +enum gba_gc_command { + GBA2_CMD_CHECK_STATE = 1, + GBA2_CMD_WRITE = 2, + GBA2_CMD_START_RECV = 4, + GBA2_CMD_VALID_CHKSUM = 6, + GBA2_CMD_INVALID_CHKSUM = 7, + GBA2_CMD_READY_FOR_XFER = 8, + GBA2_CMD_XFER_START = 9, + GBA2_CMD_XFER_FINISHED = 10, +}; + +typedef struct gba2_joywork_s { + int unused0; + + u32 packet_block_checksum; + u32 total_xfer_checksum; + int packets_processed; + int readwrite_ofs; + + int unused14; + + int cmd_attempts; + + u8 unused1C; + + u8 checksum_valid; + u8 checksum_invalid; + + u8 unused1F; + + u8 status; + u8 cmd_stage; + u8 recv_state; + u8 send_state; + + u8 port_connection_states[GBA_MAX_CHAN]; + + u8 scan_state; + u8 gba_awaiting_ack; + + int recv_wait; +} Joy_wrk_c; + +extern int Port; + +extern void mGcgba_Init(); +extern void mGcgba_InitVar(); +extern void mGcgba_EndComm(); +extern int mGcgba_ConnectEnabled(); +extern int mGcgba_Boot(u8* data_buf, size_t buf_size); +extern int mGcgba_IsEditor(); +extern int mGcgba_IsIsland(); +extern int mGcgba_Recv(u8* recv_bufp, size_t recv_buf_size); +extern int mGcgba_Read(u8* read_buf, size_t read_buf_size, u8 prog_type); +extern int mGcgba_CheckRecv(u8* recv_bufp, size_t recv_buf_size); +extern int mGcgba_Send(u8* send_bufp, size_t send_buf_size); +extern int mGcgba_send_eAppri(u8* eappli_p, size_t eappli_size); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/dolphin/dsp.h b/include/dolphin/dsp.h new file mode 100644 index 00000000..e3a5d43f --- /dev/null +++ b/include/dolphin/dsp.h @@ -0,0 +1,70 @@ +#ifndef DSP_H +#define DSP_H + +#include "types.h" +#include "dolphin/os/OSTime.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define DSP_TASK_FLAG_CLEARALL 0x00000000 +#define DSP_TASK_FLAG_ATTACHED 0x00000001 +#define DSP_TASK_FLAG_CANCEL 0x00000002 + +#define DSP_TASK_STATE_INIT 0 +#define DSP_TASK_STATE_RUN 1 +#define DSP_TASK_STATE_YIELD 2 +#define DSP_TASK_STATE_DONE 3 + +typedef void (*DSPCallback)(void* task); + +typedef struct STRUCT_DSP_TASK { + vu32 state; + vu32 priority; + vu32 flags; + u16* iram_mmem_addr; + u32 iram_length; + u32 iram_addr; + + u16* dram_mmem_addr; + u32 dram_length; + u32 dram_addr; + + u16 dsp_init_vector; + u16 dsp_resume_vector; + + DSPCallback init_cb; + DSPCallback res_cb; + DSPCallback done_cb; + DSPCallback req_cb; + + struct STRUCT_DSP_TASK* next; + struct STRUCT_DSP_TASK* prev; + + OSTime t_context; + OSTime t_task; + +} DSPTaskInfo; + +void DSPInit(); +void DSPReset(); +void DSPHalt(); +void DSPSendMailToDSP(u32 mail); +u32 DSPCheckMailToDSP(); +u32 DSPCheckMailFromDSP(); +u32 DSPGetDMAStatus(); + +DSPTaskInfo* DSPAddTask(DSPTaskInfo* task); + +void __DSP_exec_task(DSPTaskInfo* curr, DSPTaskInfo* next); +void __DSP_boot_task(DSPTaskInfo* task); +void __DSP_remove_task(DSPTaskInfo* task); +void __DSP_add_task(DSPTaskInfo* task); +void __DSP_debug_printf(const char* fmt, ...); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/dolphin/os/OSReset.h b/include/dolphin/os/OSReset.h index a5d74d43..994f29c2 100644 --- a/include/dolphin/os/OSReset.h +++ b/include/dolphin/os/OSReset.h @@ -13,6 +13,19 @@ extern "C" { #define OS_RESET_HOTRESET 1 /* Soft reset */ #define OS_RESET_SHUTDOWN 2 +typedef BOOL (*OSResetFunction)(BOOL final); +typedef struct OSResetFunctionInfo OSResetFunctionInfo; + +struct OSResetFunctionInfo { + // public + OSResetFunction func; + u32 priority; + + // private + OSResetFunctionInfo* next; + OSResetFunctionInfo* prev; +}; + u32 OSGetResetCode(); void OSResetSystem(int reset, u32 resetCode, BOOL forceMenu); BOOL OSGetResetSwitchState(); diff --git a/include/dolphin/os/OSSerial.h b/include/dolphin/os/OSSerial.h new file mode 100644 index 00000000..254cec0a --- /dev/null +++ b/include/dolphin/os/OSSerial.h @@ -0,0 +1,70 @@ +#ifndef DOLPHIN_OSSERIAL +#define DOLPHIN_OSSERIAL + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define SI_MAX_CHAN 4 +#define SI_MAX_COMCSR_INLNGTH 128 +#define SI_MAX_COMCSR_OUTLNGTH 128 +#define SI_ERROR_UNDER_RUN 0x0001 +#define SI_ERROR_OVER_RUN 0x0002 +#define SI_ERROR_COLLISION 0x0004 +#define SI_ERROR_NO_RESPONSE 0x0008 +#define SI_ERROR_WRST 0x0010 +#define SI_ERROR_RDST 0x0020 +#define SI_ERROR_UNKNOWN 0x0040 +#define SI_ERROR_BUSY 0x0080 +#define SI_CHAN0 0 +#define SI_CHAN1 1 +#define SI_CHAN2 2 +#define SI_CHAN3 3 +#define SI_CHAN0_BIT 0x80000000 +#define SI_CHAN1_BIT 0x40000000 +#define SI_CHAN2_BIT 0x20000000 +#define SI_CHAN3_BIT 0x10000000 +#define SI_CHAN_BIT(chan) (SI_CHAN0_BIT >> (chan)) +#define SI_TYPE_MASK 0x18000000u +#define SI_TYPE_N64 0x00000000u +#define SI_TYPE_DOLPHIN 0x08000000u +#define SI_TYPE_GC SI_TYPE_DOLPHIN +#define SI_GC_WIRELESS 0x80000000 +#define SI_GC_NOMOTOR 0x20000000 +#define SI_GC_STANDARD 0x01000000 +#define SI_WIRELESS_RECEIVED 0x40000000 +#define SI_WIRELESS_IR 0x04000000 +#define SI_WIRELESS_STATE 0x02000000 +#define SI_WIRELESS_ORIGIN 0x00200000 +#define SI_WIRELESS_FIX_ID 0x00100000 +#define SI_WIRELESS_TYPE 0x000f0000 +#define SI_WIRELESS_LITE_MASK 0x000c0000 +#define SI_WIRELESS_LITE 0x00040000 +#define SI_WIRELESS_CONT_MASK 0x00080000 +#define SI_WIRELESS_CONT 0x00000000 +#define SI_WIRELESS_ID 0x00c0ff00 +#define SI_WIRELESS_TYPE_ID (SI_WIRELESS_TYPE | SI_WIRELESS_ID) +#define SI_N64_CONTROLLER (SI_TYPE_N64 | 0x05000000) +#define SI_N64_MIC (SI_TYPE_N64 | 0x00010000) +#define SI_N64_KEYBOARD (SI_TYPE_N64 | 0x00020000) +#define SI_N64_MOUSE (SI_TYPE_N64 | 0x02000000) +#define SI_GBA (SI_TYPE_N64 | 0x00040000) +#define SI_GC_CONTROLLER (SI_TYPE_GC | SI_GC_STANDARD) +#define SI_GC_RECEIVER (SI_TYPE_GC | SI_GC_WIRELESS) +#define SI_GC_WAVEBIRD \ + (SI_TYPE_GC | SI_GC_WIRELESS | SI_GC_STANDARD | SI_WIRELESS_STATE | SI_WIRELESS_FIX_ID) +#define SI_GC_KEYBOARD (SI_TYPE_GC | 0x00200000) +#define SI_GC_STEERING (SI_TYPE_GC | 0x00000000) + +u32 SIProbe(s32 chan); +char* SIGetTypeString(u32 type); +void SIRefreshSamplingRate(void); +void SISetSamplingRate(u32 msec); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/dolphin/sipriv.h b/include/dolphin/sipriv.h new file mode 100644 index 00000000..e4f6ddf1 --- /dev/null +++ b/include/dolphin/sipriv.h @@ -0,0 +1,56 @@ +#ifndef DOLPHIN_SIPRIV +#define DOLPHIN_SIPRIV + +#include "types.h" +#include "dolphin/os.h" +#include "dolphin/os/OSTime.h" +#include "dolphin/os/OSInterrupt.h" +#include "dolphin/os/OSSerial.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*SICallback)(s32 chan, u32 sr, OSContext* context); +typedef void (*SITypeAndStatusCallback)(s32 chan, u32 type); + +typedef struct SIPacket { + s32 chan; + void* output; + u32 outputBytes; + void* input; + u32 inputBytes; + SICallback callback; + OSTime fire; +} SIPacket; + +void SIInit(void); +u32 SIGetStatus(s32 chan); + +BOOL SIBusy(void); +BOOL SIIsChanBusy(s32 chan); + +BOOL SITransfer(s32 chan, void* output, u32 outputBytes, void* input, u32 inputBytes, + SICallback callback, OSTime delay); +u32 SISync(void); + +void SISetCommand(s32 chan, u32 command); +u32 SIGetCommand(s32 chan); +void SITransferCommands(void); +u32 SISetXY(u32 x, u32 y); +u32 SIEnablePolling(u32 poll); +u32 SIDisablePolling(u32 poll); +BOOL SIGetResponse(s32 chan, void* data); + +BOOL SIRegisterPollingHandler(OSInterruptHandler handler); +BOOL SIUnregisterPollingHandler(OSInterruptHandler handler); + +u32 SIGetType(s32 chan); +u32 SIGetTypeAsync(s32 chan, SITypeAndStatusCallback callback); +u32 SIDecodeType(u32 type); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/gamealloc.h b/include/gamealloc.h index 5101730f..58d769dd 100644 --- a/include/gamealloc.h +++ b/include/gamealloc.h @@ -22,6 +22,9 @@ typedef struct gameAlloc_s { } GameAlloc; extern void* gamealloc_malloc(GameAlloc* gamealloc, size_t size); +extern void gamealloc_free(GameAlloc* gamealloc, void* ptr); +extern void gamealloc_cleanup(GameAlloc* gamealloc); +extern void gamealloc_init(GameAlloc* gamealloc); #ifdef __cplusplus }; diff --git a/src/GBA/GBA.c b/src/GBA/GBA.c new file mode 100644 index 00000000..1be09dd0 --- /dev/null +++ b/src/GBA/GBA.c @@ -0,0 +1,93 @@ +#include "GBA/GBAPriv.h" + +static GBASecParam SecParams[4]; +GBAControl __GBA[4]; +BOOL __GBAReset = FALSE; + +static BOOL OnReset(BOOL); + +static OSResetFunctionInfo ResetFunctionInfo = {OnReset, 127}; + +static void ShortCommandProc(s32 chan) { + GBAControl* gba; + gba = &__GBA[chan]; + + if (gba->ret != 0) { + return; + } + + if (gba->input[0] != 0 || gba->input[1] != 4) { + gba->ret = 1; + return; + } + + gba->status[0] = gba->input[2] & GBA_JSTAT_MASK; +} + +void GBAInit() { + GBAControl* gba; + s32 chan; + + for (chan = 0; chan < 4; ++chan) { + gba = &__GBA[chan]; + gba->delay = OSMicrosecondsToTicks(60); + OSInitThreadQueue(&gba->threadQueue); + gba->param = &SecParams[chan]; + } + + OSInitAlarm(); + DSPInit(); + + __GBAReset = FALSE; + OSRegisterResetFunction(&ResetFunctionInfo); +} + +s32 GBAGetStatusAsync(s32 chan, u8* status, GBACallback callback) { + GBAControl* gba; + gba = &__GBA[chan]; + if (gba->callback != NULL) { + return GBA_BUSY; + } + + gba->output[0] = 0; + gba->status = status; + gba->callback = callback; + return __GBATransfer(chan, 1, 3, ShortCommandProc); +} + +s32 GBAGetStatus(s32 chan, u8* status) { + GBAControl* gba = &__GBA[chan]; + s32 ret = GBAGetStatusAsync(chan, status, __GBASyncCallback); + if (ret != GBA_READY) { + return ret; + } + return __GBASync(chan); +} + +s32 GBAResetAsync(s32 chan, u8* status, GBACallback callback) { + GBAControl* gba = &__GBA[chan]; + s32 ret; + if (gba->callback != NULL) { + return GBA_BUSY; + } + gba->output[0] = 0xFF; + gba->status = status; + gba->callback = callback; + return __GBATransfer(chan, 1, 3, ShortCommandProc); +} + +s32 GBAReset(s32 chan, u8* status) { + GBAControl* gba = &__GBA[chan]; + s32 ret; + + ret = GBAResetAsync(chan, status, __GBASyncCallback); + if (ret != GBA_READY) { + return ret; + } + return __GBASync(chan); +} + +BOOL OnReset(BOOL unused) { + __GBAReset = TRUE; + return TRUE; +} \ No newline at end of file diff --git a/src/GBA/GBAGetProcessStatus.c b/src/GBA/GBAGetProcessStatus.c new file mode 100644 index 00000000..00e76837 --- /dev/null +++ b/src/GBA/GBAGetProcessStatus.c @@ -0,0 +1,39 @@ +#include "GBA/GBAPriv.h" + +s32 GBAGetProcessStatus(s32 chan, u8* percentp) { + BOOL enabled; + s32 ret; // r29 + GBAControl* gba = &__GBA[chan]; // r25 + GBABootInfo* bootInfo; // r31 + u8 percent; // r30 + OSTime t; // r27 + + bootInfo = &gba->bootInfo; + enabled = OSDisableInterrupts(); + + if (bootInfo->callback != NULL) { + ret = 2; + percent = (bootInfo->curOffset * 100) / bootInfo->realLength; + if (bootInfo->begin != 0) { + t = OSGetTime(); + if (OSTicksToMilliseconds(t - bootInfo->begin) < (5500)) { + percent = ((t - bootInfo->begin) * percent) / OSMillisecondsToTicks((OSTime)5500); + } + + if (percent >= 100) { + percent = 100; + } + } + + if (percentp != NULL) { + *percentp = percent; + } + } else if (gba->callback != NULL) { + ret = 2; + } else { + ret = 0; + } + + OSRestoreInterrupts(enabled); + return ret; +} diff --git a/src/GBA/GBAJoyBoot.c b/src/GBA/GBAJoyBoot.c new file mode 100644 index 00000000..424547cb --- /dev/null +++ b/src/GBA/GBAJoyBoot.c @@ -0,0 +1,375 @@ +#include "GBA/GBAPriv.h" + +static volatile u8 D54[] = { + 0x18, 0xFC, 0xC0, 0x80, 0x7f, 0x40, 0x3f, 0x01, 0x00, 0x2f, 0x2f, 0x20, 0x43, 0x6f, 0x64, + 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x4b, 0x61, 0x77, 0x61, 0x73, 0x65, 0x64, 0x6f, 0x00, + 0x00, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0xAC, 0xC4, 0xF8, 0x08, 0x10, 0xBF, 0x18, +}; + +static void F23(s32 chan, s32 ret); +static void F25(s32 chan, s32 ret); +static void F27(s32 chan, s32 ret); +static void F29(s32 chan, s32 ret); +static void F31(s32 chan, s32 ret); +static void F33(s32 chan, s32 ret); +static void F35(s32 chan, s32 ret); +static void F37(s32 chan, s32 ret); +static void F39(s32 chan, s32 ret); + +static u32 F72(u32 crc, u32 src, vu8* keyp) { + int i; // r31 + int poly = + (keyp[0x13] << 8) + ((keyp[0x15] + (keyp[0x18] - (keyp[0x18] << 4))) - keyp[0x10]); // r30 + for (i = keyp[1]; i > keyp[11]; --i) { + if ((src ^ crc) & 1) { + crc >>= 1; + crc ^= poly; + } else { + crc >>= 1; + } + + src >>= 1; + } + + return crc; +} + +static u32 F95(u32 src, vu8* keyp) { + src = (src * ((keyp[3] << keyp[0x16]) | ((keyp[1] | (keyp[4] << keyp[0x11])) | (keyp[4] << keyp[0x18])))) - (keyp[7] - keyp[6]); + return src; +} + +static void F104(s32 chan, s32 ret) { + GBABootInfo* bootInfo; + GBACallback callback; + + bootInfo = &__GBA[chan].bootInfo; + bootInfo->begin = 0; + if (bootInfo->callback != NULL) { + callback = bootInfo->callback; + bootInfo->callback = NULL; + callback(chan, ret); + } +} + +s32 GBAJoyBootAsync(s32 chan, s32 paletteColor, s32 paletteSpeed, u8* programp, s32 length, + u8* status, GBACallback callback) { + int ret; + GBABootInfo* bootInfo = &__GBA[chan].bootInfo; + u8 percent; + if (chan & ~3) { + return GBA_JOYBOOT_ERR_INVALID; + } else if (length == 0 || GBA_JOYBOOT_PROGRAM_SIZE_MAX < length) { + return GBA_JOYBOOT_ERR_INVALID; + } else if (paletteSpeed < -4 || paletteSpeed > 4) { + return GBA_JOYBOOT_ERR_INVALID; + } else if (paletteColor < 0 || paletteColor > 6) { + return GBA_JOYBOOT_ERR_INVALID; + } else if (programp[0xac] * programp[0xad] * programp[0xae] * programp[0xaf] == 0) { + return GBA_JOYBOOT_ERR_INVALID; + } + + ret = GBAGetProcessStatus(chan, &percent); + if (ret != 0) { + return ret; + } + + bootInfo->paletteColor = paletteColor; + bootInfo->paletteSpeed = paletteSpeed; + bootInfo->programp = programp; + bootInfo->length = length; + bootInfo->status = status; + bootInfo->callback = callback; + bootInfo->curOffset = D54[8]; + ret = GBAGetStatusAsync(chan, bootInfo->status, F23); + if (ret != GBA_READY) { + bootInfo->callback = NULL; + } + + return ret; +} +static void F23(s32 chan, s32 ret) { + GBAControl* gba; + GBABootInfo* bootInfo; + + gba = &__GBA[chan]; + bootInfo = &__GBA[chan].bootInfo; + + if (ret != GBA_READY || (ret = GBAResetAsync(chan, bootInfo->status, F25)) != GBA_READY) { + F104(chan, ret); + } + + gba->ret = ret; +} + +static void F25(s32 chan, s32 ret) { + GBAControl* gba = &__GBA[chan]; + GBABootInfo* bootInfo = &__GBA[chan].bootInfo; + + if (ret == GBA_READY && *bootInfo->status != D54[37]) { + ret = GBA_JOYBOOT_UNKNOWN_STATE; + } + + if (ret != GBA_READY || (ret = GBAGetStatusAsync(chan, bootInfo->status, F27))) { + F104(chan, ret); + } + + gba->ret = ret; +} + +static void F27(s32 chan, s32 ret) { + GBAControl* gba = &__GBA[chan]; + GBABootInfo* bootInfo = &__GBA[chan].bootInfo; + + if (ret == GBA_READY && *bootInfo->status != D54[0]) { + ret = GBA_JOYBOOT_UNKNOWN_STATE; + } + + if (ret != GBA_READY || (ret = GBAReadAsync(chan, bootInfo->readbuf, bootInfo->status, F29))) { + F104(chan, ret); + } + + gba->ret = ret; +} + +static void F29(s32 chan, s32 ret) { + GBAControl* gba = &__GBA[chan]; + GBABootInfo* bootInfo = &__GBA[chan].bootInfo; + + if (ret == GBA_READY) { + __GBAX02(chan, bootInfo->readbuf); + } else { + F104(chan, ret); + } + + gba->ret = ret; +} +void __GBAX01(s32 chan, s32 ret) { + GBAControl* gba = &__GBA[chan]; + GBABootInfo* bootInfo = &__GBA[chan].bootInfo; + int val200; + + if (ret == GBA_READY) { + bootInfo->keyA = gba->param->keyA; + bootInfo->keyB = gba->param->keyB; + if (bootInfo->readbuf[3] == 0 || bootInfo->readbuf[2] == 0 || + (bootInfo->keyA & (D54[5] << 9)) == 0 || bootInfo->readbuf[1] == 0 || + (bootInfo->keyA >> 15) == 0 || bootInfo->readbuf[0] == 0) { + ret = GBA_JOYBOOT_UNKNOWN_STATE; + } else { + bootInfo->i = ~D54[36] & bootInfo->length + D54[36]; + val200 = D54[20] << D54[33]; + + if (bootInfo->i < val200) { + bootInfo->i = val200; + } + + bootInfo->realLength = bootInfo->i; + bootInfo->i -= val200; + bootInfo->i >>= D54[32]; + bootInfo->writebuf[0] = (u8)(bootInfo->keyB >> 0); + bootInfo->writebuf[1] = (u8)(bootInfo->keyB >> 8); + bootInfo->writebuf[3] = (u8)(bootInfo->keyB >> 24); + bootInfo->writebuf[2] = (u8)(bootInfo->keyB >> 16); + bootInfo->crc = (D54[38] + 1) << D54[34]; + bootInfo->curOffset = D54[8]; + bootInfo->begin = OSGetTime(); + bootInfo->firstXfer = TRUE; + ret = GBAWriteAsync(chan, bootInfo->writebuf, bootInfo->status, F31); + } + } + + if (ret != GBA_READY) { + F104(chan, ret); + } + gba->ret = ret; +} + +static void F31(s32 chan, s32 ret) { + GBAControl* gba; + GBABootInfo* bootInfo; + u32 writeWord; + + gba = &__GBA[chan]; + bootInfo = &__GBA[chan].bootInfo; + + if (ret == GBA_READY) { + if (bootInfo->firstXfer != FALSE) { + bootInfo->firstXfer = FALSE; + } else if ((*bootInfo->status & D54[20]) == 0 || + (*bootInfo->status & D54[42]) >> D54[33] != + (bootInfo->curOffset & D54[33]) >> D54[31]) { + ret = GBA_JOYBOOT_UNKNOWN_STATE; + goto exit; + } else { + bootInfo->curOffset -= D54[25] - D54[23]; + } + + if (bootInfo->curOffset <= bootInfo->realLength) { + if (bootInfo->curOffset < bootInfo->realLength) { + writeWord = D54[29]; + for (bootInfo->i = D54[29]; bootInfo->i < D54[33]; ++bootInfo->i) { + if (bootInfo->length != 0) { + writeWord |= *(bootInfo->programp++) << (bootInfo->i * D54[37]); + --bootInfo->length; + } + } + + if (bootInfo->curOffset == D54[38]) { + bootInfo->initialCode = writeWord; + } else if (bootInfo->curOffset == D54[39]) { + writeWord = chan << D54[37]; + } + + if (bootInfo->curOffset >= D54[2]) { + bootInfo->crc = F72(bootInfo->crc, writeWord, &D54[19]); + } + + if (bootInfo->curOffset == D54[40] + 0x100) { + bootInfo->dummyWord[0] = writeWord; + } else if (bootInfo->curOffset == D54[1] + 0x100) { + bootInfo->i = D54[7]; + bootInfo->dummyWord[bootInfo->i] = writeWord; + } + } else { + writeWord = bootInfo->crc | (bootInfo->curOffset << 16); + } + + if (bootInfo->curOffset > D54[43]) { + bootInfo->keyA = F95(bootInfo->keyA, &D54[20]); + writeWord ^= bootInfo->keyA; + writeWord ^= -(bootInfo->curOffset + D54[11] * 0x100000); + writeWord ^= (D54[11] | (D54[11] << 24)) | (D54[19] << 16) | (D54[18] << 8); + } + + bootInfo->writebuf[3] = (writeWord >> D54[0]); + bootInfo->writebuf[0] = (writeWord >> D54[30]); + bootInfo->writebuf[1] = (writeWord >> D54[41]); + bootInfo->writebuf[2] = (writeWord >> D54[42]); + + if (bootInfo->curOffset == D54[1] + D54[1]) { + bootInfo->dummyWord[2] = writeWord; + } + + if (bootInfo->i < D54[33]) { + bootInfo->dummyWord[3 - (1 - bootInfo->i)] = writeWord; + bootInfo->dummyWord[5 - bootInfo->i] = + bootInfo->dummyWord[(2 - (1 - bootInfo->i))] * bootInfo->dummyWord[4 - bootInfo->i]; + bootInfo->dummyWord[5 - (1 - bootInfo->i)] = + bootInfo->dummyWord[((1 - bootInfo->i))] * bootInfo->dummyWord[1 - (1 - bootInfo->i)]; + bootInfo->dummyWord[7 - bootInfo->i] = + bootInfo->dummyWord[(-(1 - bootInfo->i))] * bootInfo->dummyWord[4 - bootInfo->i]; + } + + ret = GBAWriteAsync(chan, bootInfo->writebuf, bootInfo->status, F31); + } else { + bootInfo->start = OSGetTick(); + ret = GBAReadAsync(chan, bootInfo->readbuf, bootInfo->status, F33); + } + } +exit: + if (ret != GBA_READY) { + F104(chan, ret); + } + + gba->ret = ret; +} +static void F33(s32 chan, s32 ret) { + GBAControl* gba = &__GBA[chan]; + GBABootInfo* bootInfo = &__GBA[chan].bootInfo; + + if (ret == GBA_READY) { + for (bootInfo->i = 33; bootInfo->i < 36; ++bootInfo->i) { + ret = (((bootInfo->readbuf[3] ^ (bootInfo->dummyWord[D54[bootInfo->i]] >> 24)) | + (bootInfo->readbuf[2] ^ (bootInfo->dummyWord[D54[bootInfo->i]] >> 16)) | + (bootInfo->readbuf[1] ^ (bootInfo->dummyWord[D54[bootInfo->i]] >> 8)) | + (bootInfo->readbuf[0] ^ (bootInfo->dummyWord[D54[bootInfo->i]] >> 0))) + << 24); + + if (ret == GBA_READY) { + break; + } + } + + if (ret != GBA_READY) { + ret = GBA_JOYBOOT_UNKNOWN_STATE; + } else { + bootInfo->start = OSGetTick(); + ret = GBAGetStatusAsync(chan, bootInfo->status, F35); + } + } + + if (ret != GBA_READY) { + F104(chan, ret); + } + + gba->ret = ret; +} +static void F35(s32 chan, s32 ret) { + GBAControl* gba = &__GBA[chan]; + GBABootInfo* bootInfo = &__GBA[chan].bootInfo; + + if (ret == 0) { + if (OSSecondsToTicks(10) <= OSGetTick() - bootInfo->start) { + ret = GBA_JOYBOOT_UNKNOWN_STATE; + } else if ((*bootInfo->status & 0x32) != 0) { + ret = GBA_JOYBOOT_UNKNOWN_STATE; + } else if (*bootInfo->status != 8) { + ret = GBAGetStatusAsync(chan, bootInfo->status, F35); + } else { + __GBASetDelay(chan, OSMillisecondsToTicks(8)); + ret = GBAReadAsync(chan, bootInfo->readbuf, bootInfo->status, F37); + __GBASetDelay(chan, OSMicrosecondsToTicks(60)); + } + } + + if (ret != GBA_READY) { + F104(chan, ret); + } + + gba->ret = ret; +} + +static void F37(s32 chan, s32 ret) { + GBAControl* gba = &__GBA[chan]; + GBABootInfo* bootInfo = &__GBA[chan].bootInfo; + + if (ret == GBA_READY) { + if ((((bootInfo->readbuf[3] ^ (bootInfo->initialCode >> 24)) | + (bootInfo->readbuf[2] ^ (bootInfo->initialCode >> 16)) | + (bootInfo->readbuf[1] ^ (bootInfo->initialCode >> 8)) | + (bootInfo->readbuf[0] ^ (bootInfo->initialCode >> 0))) + << 24) != 0) { + ret = GBA_JOYBOOT_UNKNOWN_STATE; + } else { + ret = GBAWriteAsync(chan, bootInfo->readbuf, bootInfo->status, F39); + } + } + + if (ret != GBA_READY) { + F104(chan, ret); + } + gba->ret = ret; +} + +static void F39(s32 chan, s32 ret) { + GBAControl* gba = &__GBA[chan]; + GBABootInfo* bootInfo = &__GBA[chan].bootInfo; + + if (ret == GBA_READY) { + *bootInfo->status = 0; + } + + F104(chan, ret); + gba->ret = ret; +} + +s32 GBAJoyBoot(s32 chan, s32 paletteColor, s32 paletteSpeed, u8* programp, s32 length, u8* status) { + s32 ret = GBAJoyBootAsync(chan, paletteColor, paletteSpeed, programp, length, status, + __GBASyncCallback); + if (ret != GBA_READY) { + return ret; + } + + return __GBASync(chan); +} \ No newline at end of file diff --git a/src/GBA/GBAKey.c b/src/GBA/GBAKey.c new file mode 100644 index 00000000..65888eae --- /dev/null +++ b/src/GBA/GBAKey.c @@ -0,0 +1,114 @@ +#include "GBA/GBAPriv.h" + +static volatile u8 D35[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 33, 2, 255, 0, 33, 19, 6, 18, 3, 18, 4, + 19, 5, 0, 146, 0, 255, 0, 136, 255, 255, 0, 137, 255, 255, 0, 138, 255, 255, 0, + 139, 255, 255, 143, 0, 139, 0, 140, 0, 2, 191, 128, 126, 22, 252, 220, 209, 22, 253, + 0, 0, 22, 251, 0, 1, 2, 191, 128, 120, 36, 255, 2, 128, 171, 186, 2, 148, 0, + 41, 142, 0, 2, 191, 128, 120, 32, 255, 2, 64, 15, 255, 31, 94, 0, 155, 0, 0, + 0, 153, 0, 32, 0, 135, 0, 0, 0, 128, 0, 65, 2, 159, 128, 188, 2, 191, 0, + 140, 2, 191, 128, 126, 22, 252, 220, 209, 22, 253, 0, 3, 22, 251, 0, 1, 143, 0, + 2, 191, 128, 120, 2, 128, 205, 209, 2, 148, 0, 76, 38, 255, 2, 128, 0, 1, 2, + 149, 0, 94, 2, 128, 0, 2, 2, 149, 128, 0, 2, 159, 0, 76, 0, 33, 142, 0, + 2, 191, 128, 120, 36, 255, 2, 191, 128, 120, 36, 255, 2, 191, 128, 120, 36, 255, 2, + 191, 128, 120, 0, 197, 255, 255, 2, 64, 15, 255, 28, 158, 2, 191, 128, 120, 0, 199, + 255, 255, 2, 191, 128, 120, 0, 198, 255, 255, 2, 191, 128, 120, 0, 192, 255, 255, 2, + 191, 128, 120, 32, 255, 2, 64, 15, 255, 31, 94, 2, 191, 128, 120, 33, 255, 2, 191, + 128, 120, 35, 255, 18, 5, 18, 6, 2, 159, 128, 181, 0, 33, 129, 0, 0, 129, 0, + 16, 16, 32, 27, 62, 0, 223, 20, 86, 3, 64, 255, 208, 132, 23, 0, 128, 0, 0, + 0, 134, 0, 0, 0, 130, 0, 31, 0, 222, 21, 246, 20, 8, 0, 223, 23, 102, 3, + 64, 0, 255, 31, 95, 2, 191, 136, 229, 31, 28, 129, 30, 25, 30, 20, 120, 31, 252, + 31, 94, 2, 191, 136, 9, 2, 191, 135, 35, 0, 6, 129, 6, 0, 222, 22, 108, 20, + 4, 2, 64, 255, 0, 0, 223, 18, 49, 21, 120, 3, 64, 0, 255, 31, 95, 2, 191, + 136, 229, 31, 28, 129, 30, 25, 30, 20, 120, 31, 252, 31, 94, 2, 191, 136, 9, 2, + 191, 135, 35, 129, 0, 137, 0, 0, 209, 0, 5, 153, 0, 130, 0, 2, 149, 0, 229, + 2, 145, 0, 243, 0, 130, 0, 16, 0, 134, 0, 1, 0, 208, 23, 27, 145, 0, 125, + 0, 77, 0, 21, 1, 31, 95, 0, 223, 0, 3, 21, 4, 2, 191, 136, 9, 2, 159, + 1, 2, 0, 130, 0, 17, 0, 223, 0, 3, 21, 1, 31, 95, 0, 222, 16, 67, 2, + 64, 255, 240, 2, 191, 136, 229, 2, 159, 1, 2, 0, 130, 0, 16, 0, 134, 0, 1, + 0, 208, 18, 133, 145, 0, 77, 0, 21, 1, 0, 222, 0, 3, 20, 4, 31, 94, 2, + 191, 136, 9, 0, 131, 0, 19, 27, 126, 137, 35, 0, 131, 0, 19, 0, 223, 0, 7, + 0, 222, 17, 184, 2, 64, 255, 240, 31, 94, 2, 191, 129, 244, 241, 0, 2, 191, 132, + 88, 143, 0, 0, 130, 0, 21, 0, 222, 0, 6, 0, 218, 22, 91, 2, 191, 136, 229, + 20, 253, 20, 3, 27, 94, 27, 92, 0, 130, 0, 22, 0, 222, 23, 35, 20, 244, 0, + 218, 22, 107, 2, 191, 136, 229, 177, 0, 2, 144, 1, 46, 129, 0, 20, 253, 142, 0, + 0, 223, 20, 145, 3, 64, 208, 240, 28, 191, 0, 223, 20, 104, 0, 209, 17, 252, 21, + 124, 28, 223, 0, 209, 17, 184, 153, 0, 20, 24, 20, 120, 31, 94, 31, 254, 31, 101, + 54, 0, 20, 2, 31, 102, 55, 0, 21, 1, 76, 0, 21, 24, 153, 0, 53, 0, 76, + 0, 0, 223, 0, 18, 63, 0, 0, 255, 0, 18, 20, 112, 0, 223, 0, 17, 63, 0, + 0, 255, 0, 17, 31, 165, 21, 1, 31, 230, 241, 0, 21, 248, 245, 0, 31, 95, 31, + 125, 129, 0, 0, 222, 0, 17, 52, 0, 137, 0, 0, 223, 0, 18, 53, 0, 76, 0, + 0, 223, 0, 18, 21, 120, 76, 0, 137, 0, 31, 254, 21, 8, 59, 0, 0, 222, 0, + 17, 62, 0, 0, 223, 0, 18, 59, 0, 28, 191, 0, 218, 21, 241, 53, 0, 2, 149, + 1, 146, 0, 223, 16, 226, 21, 8, 31, 95, 0, 223, 16, 59, 121, 0, 57, 0, 48, + 128, 0, 254, 0, 34, 0, 220, 18, 41, 0, 221, 17, 248, 92, 0, 240, 0, 31, 229, + 48, 128, 2, 159, 1, 165, 0, 223, 16, 202, 21, 8, 31, 95, 0, 223, 16, 67, 117, + 0, 57, 0, 48, 128, 0, 254, 0, 34, 0, 220, 18, 89, 0, 221, 22, 254, 76, 0, + 240, 0, 31, 229, 48, 128, 0, 254, 0, 35, 0, 218, 0, 8, 0, 216, 0, 9, 0, + 155, 0, 32, 0, 153, 0, 8, 0, 135, 0, 0, 2, 191, 128, 139, 2, 223, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, +}; + +static s32 F152(DSPTaskInfo* task) { + s32 chan; // r31 + GBAControl* gba; // r29 + + for (chan = 0; chan < 4; ++chan) { + gba = &__GBA[chan]; + if (&gba->task == task) { + return chan; + } + } + + ASSERT(0 <= chan && chan < 4); + OSPanic(__FILE__, 169, "GBA - unexpected dsp call"); + return -1; +} + +static void F23(void* task) { + s32 chan; // r31 + GBAControl* gba; // r30 + chan = F152(task); + gba = &__GBA[chan]; + DSPSendMailToDSP(0xabba0000); + while (DSPCheckMailToDSP()) {} + DSPSendMailToDSP((u32)gba->param); + while (DSPCheckMailToDSP()) {} +} + +void F25(void* task) { + s32 chan; // r31 + chan = F152(task); + __GBAX01(chan, 0); +} + +void __GBAX02(s32 chan, u8* readbuf) { + GBAControl* gba; // r28 + GBABootInfo* bootInfo; // r29 + GBASecParam* param; // r30 + DSPTaskInfo* task; // r31 + + gba = &__GBA[chan]; + bootInfo = &__GBA[chan].bootInfo; + param = gba->param; + memcpy(param, readbuf, 4); + param->paletteColor = bootInfo->paletteColor; + param->paletteSpeed = bootInfo->paletteSpeed; + param->length = bootInfo->length; + param->out = ¶m->keyA; + DCInvalidateRange(¶m->keyA, 32); + DCFlushRange(param, 32); + task = &gba->task; + task->priority = 255; + task->iram_mmem_addr = (u16*)OSCachedToPhysical(D35); + task->iram_length = sizeof(D35); + task->iram_addr = 0; + task->dsp_init_vector = 16; + task->init_cb = F23; + task->res_cb = NULL; + task->done_cb = F25; + task->req_cb = NULL; + DSPAddTask(task); +} diff --git a/src/GBA/GBARead.c b/src/GBA/GBARead.c new file mode 100644 index 00000000..991e5121 --- /dev/null +++ b/src/GBA/GBARead.c @@ -0,0 +1,40 @@ +#include "GBA/GBAPriv.h" + +static void ReadProc(s32 chan) { + GBAControl* gba; + gba = &__GBA[chan]; + + if (gba->ret == 0) { + memcpy(gba->ptr, gba->input, 4); + gba->status[0] = gba->input[4] & GBA_JSTAT_MASK; + } +} + +s32 GBAReadAsync(s32 chan, u8* dst, u8* status, GBACallback callback) { + GBAControl* gba; + + gba = &__GBA[chan]; + + if (gba->callback != NULL) { + return 2; + } + + gba->output[0] = 0x14; + gba->ptr = dst; + gba->status = status; + gba->callback = callback; + + return __GBATransfer(chan, 1, 5, ReadProc); +} + +s32 GBARead(s32 chan, u8* dst, u8* status) { + s32 tmp; + GBAControl* gba = &__GBA[chan]; + s32 ret; + ret = GBAReadAsync(chan, dst, status, __GBASyncCallback); + if (ret != GBA_READY) { + return ret; + } + + return __GBASync(chan); +} diff --git a/src/GBA/GBAWrite.c b/src/GBA/GBAWrite.c new file mode 100644 index 00000000..b8ba9bc0 --- /dev/null +++ b/src/GBA/GBAWrite.c @@ -0,0 +1,37 @@ +#include "GBA/GBAPriv.h" + +static void WriteProc(s32 chan) { + GBAControl* gba; + gba = &__GBA[chan]; + + if (gba->ret != 0) { + return; + } + + gba->status[0] = gba->input[0] & GBA_JSTAT_MASK; +} + +s32 GBAWriteAsync(s32 chan, u8* src, u8* status, GBACallback callback) { + GBAControl* gba = &__GBA[chan]; + + if (gba->callback != NULL) { + return GBA_BUSY; + } + gba->output[0] = 0x15; + memcpy(&gba->output[1], src, 4); + gba->ptr = src; + gba->status = status; + gba->callback = callback; + return __GBATransfer(chan, 5, 1, WriteProc); +} + +s32 GBAWrite(s32 chan, u8* src, u8* status) { + s32 tmp; + GBAControl* gba = &__GBA[chan]; + s32 ret; + ret = GBAWriteAsync(chan, src, status, __GBASyncCallback); + if (ret != GBA_READY) { + return ret; + } + return __GBASync(chan); +} diff --git a/src/GBA/GBAXfer.c b/src/GBA/GBAXfer.c new file mode 100644 index 00000000..c44346f6 --- /dev/null +++ b/src/GBA/GBAXfer.c @@ -0,0 +1,124 @@ +#include "GBA/GBAPriv.h" +#include "dolphin/sipriv.h" + +static void __GBAHandler(s32 chan, u32 error, OSContext* context) { + GBAControl* gba; + GBATransferCallback proc; + GBACallback callback; + OSContext exceptionContext; + + gba = &__GBA[chan]; + if (__GBAReset != 0) { + return; + } + + if ((error & 0xf)) { + gba->ret = 1; + } else { + gba->ret = 0; + } + + if (gba->proc != NULL) { + proc = gba->proc; + gba->proc = NULL; + proc(chan); + } + + if (gba->callback == NULL) { + return; + } + + OSClearContext(&exceptionContext); + OSSetCurrentContext(&exceptionContext); + callback = gba->callback; + gba->callback = NULL; + callback(chan, gba->ret); + OSClearContext(&exceptionContext); + OSSetCurrentContext(context); +} + +void __GBASyncCallback(s32 chan, s32 ret) { + GBAControl* gba = &__GBA[chan]; + OSWakeupThread(&gba->threadQueue); +} + +s32 __GBASync(s32 chan) { + GBAControl* gba; + s32 ret; + s32 enabled; + gba = &__GBA[chan]; + + enabled = OSDisableInterrupts(); + while (gba->callback != NULL) { + OSSleepThread(&gba->threadQueue); + } + + ret = gba->ret; + OSRestoreInterrupts(enabled); + + return ret; +} + +void TypeAndStatusCallback(s32 chan, u32 type) { + GBAControl* gba; + GBATransferCallback proc; + GBACallback callback; + OSContext exceptionContext; + OSContext* context; + + gba = &__GBA[chan]; + if (__GBAReset != 0) { + return; + } + + if ((type & 0xFF) != 0 || (type & 0xffff0000) != 0x40000) { + gba->ret = GBA_NOT_READY; + } else { + if (SITransfer(chan, gba->output, gba->outputBytes, gba->input, gba->inputBytes, __GBAHandler, + gba->delay)) { + return; + } + gba->ret = GBA_BUSY; + } + + if (gba->proc != NULL) { + proc = gba->proc; + gba->proc = NULL; + proc(chan); + } + + if (gba->callback != NULL) { + context = OSGetCurrentContext(); + OSClearContext(&exceptionContext); + OSSetCurrentContext(&exceptionContext); + callback = gba->callback; + gba->callback = NULL; + callback(chan, gba->ret); + OSClearContext(&exceptionContext); + OSSetCurrentContext(context); + __OSReschedule(); + } +} + +s32 __GBATransfer(s32 chan, s32 w1, s32 w2, GBATransferCallback callback) { + s32 enabled; + GBAControl* gba; + gba = &__GBA[chan]; + enabled = OSDisableInterrupts(); + gba->proc = callback; + gba->outputBytes = w1; + gba->inputBytes = w2; + SIGetTypeAsync(chan, TypeAndStatusCallback); + OSRestoreInterrupts(enabled); + + return GBA_READY; +} + +OSTime __GBASetDelay(s32 chan, OSTime delay) { + OSTime oldDelay; + GBAControl* gba; + gba = &__GBA[chan]; + oldDelay = gba->delay; + gba->delay = delay; + return oldDelay; +} diff --git a/src/GBA2/JoyBoot.c b/src/GBA2/JoyBoot.c new file mode 100644 index 00000000..0ddda435 --- /dev/null +++ b/src/GBA2/JoyBoot.c @@ -0,0 +1,955 @@ +#include "GBA2/gba2.h" + +#include "_mem.h" +#include "MSL_C/printf.h" + +static Joy_wrk_c g_Joy_wrk; +int Port; + +static u32 mJOYSIO_ConvData(u8* data) { + u8 conv_buf[sizeof(u32)]; + + conv_buf[0] = data[3]; + conv_buf[1] = data[2]; + conv_buf[2] = data[1]; + conv_buf[3] = data[0]; + + return *(u32*)conv_buf; +} + +extern void mGcgba_Init() { } + +extern void mGcgba_InitVar() { + memset(&g_Joy_wrk, 0, sizeof(Joy_wrk_c)); +} + +extern void mGcgba_EndComm() { + Port = -1; +} + +extern int mGcgba_ConnectEnabled() { + int state = GBA2_GBA_STATE_TRANSMITTING; + + if (g_Joy_wrk.cmd_attempts < GBA2_CONNECTED_TRIES) { + int port; + + for (port = 0; port < GBA_MAX_CHAN; port++) { + u8 status; + + if (GBAGetStatus(port, &status) == GBA_READY) { + state = GBA2_GBA_STATE_SUCCESS; + Port = port; + break; + } + } + + if (state != GBA2_GBA_STATE_SUCCESS) { + Port = -1; + } + } + else { + state = GBA2_GBA_STATE_ERROR; + } + + g_Joy_wrk.cmd_attempts++; + return state; +} + +static void CallBack(s32 port, s32 ret) { + g_Joy_wrk.port_connection_states[port] = ret; +} + +extern int mGcgba_Boot(u8* data_buf, size_t buf_size) { + static u8 state; + int gba_state = GBA2_GBA_STATE_TRANSMITTING; + + if (Port < GBA_CHAN0 || Port >= GBA_MAX_CHAN) { + return GBA2_GBA_STATE_ERROR; + } + else { + if (g_Joy_wrk.cmd_attempts < GBA2_DEFAULT_TRIES) { + switch (g_Joy_wrk.cmd_stage) { + case 0: + { + int port; + for (port = 0; port < GBA_MAX_CHAN; port++) { + g_Joy_wrk.port_connection_states[port] = GBA2_GBA_STATE_SUCCESS; + } + + g_Joy_wrk.cmd_stage++; + break; + } + + case 1: + { + if (GBAGetProcessStatus(Port, NULL) == GBA_BUSY) { + g_Joy_wrk.cmd_attempts = 0; + } + else if (g_Joy_wrk.port_connection_states[Port] == GBA_READY) { + gba_state = GBA2_GBA_STATE_SUCCESS; + } + else { + if (GBAJoyBootAsync(Port, 1, 2, data_buf, buf_size, &state, CallBack)) { + g_Joy_wrk.port_connection_states[Port] = GBA2_GBA_STATE_TRANSMITTING; + } + } + break; + } + } + } + else { + gba_state = GBA2_GBA_STATE_ERROR; + } + + g_Joy_wrk.cmd_attempts++; + } + + return gba_state; +} + +static int mJOYSIO_OpenConnection(Joy_wrk_c* joywork) { + int gba_state = GBA2_GBA_STATE_TRANSMITTING; + u8 status; + + if (GBAGetStatus(Port, &status) != GBA_READY) { + gba_state = GBA2_GBA_STATE_ERROR; + } + else { + if (joywork->gba_awaiting_ack == TRUE) { + u32 data = mJOYSIO_ConvData((u8*)"GAFJ"); + + if (GBAWrite(Port, (u8*)&data, &status) == GBA_READY) { + gba_state = GBA2_GBA_STATE_SUCCESS; + } + else { + gba_state = GBA2_GBA_STATE_ERROR; + } + } + else { + if (status & GBA_JSTAT_SEND) { + u32 recv; + if (GBARead(Port, (u8*)&recv, &status) == GBA_READY) { + if (*(u32*)"AAFJ" == recv) { + joywork->gba_awaiting_ack = TRUE; + } + } + else { + gba_state = GBA2_GBA_STATE_ERROR; + } + } + + if (gba_state == GBA2_GBA_STATE_TRANSMITTING && joywork->gba_awaiting_ack == FALSE && GBAReset(Port, &status) != GBA_READY) { + gba_state = GBA2_GBA_STATE_ERROR; + } + } + } + + return gba_state; +} + +static int mGcgba_StartScan(Joy_wrk_c* joywork, u32 cmd) { + int gba_state = GBA2_GBA_STATE_TRANSMITTING; + u8 status; + u8 status2; + u32 send; + u32 recv; + + if (GBAGetStatus(Port, &status) == GBA_READY) { + if (joywork->scan_state == 1) { + if (status & GBA_JSTAT_SEND) { + + if (GBARead(Port, (u8*)&recv, &status2) != GBA_READY) { + gba_state = GBA2_GBA_STATE_ERROR; + } + else { + switch (mJOYSIO_ConvData((u8*)&recv)) { + case 0xFFFE0208: + { + gba_state = GBA2_GBA_STATE_SUCCESS; + break; + } + + case 0xFFFE020A: + { + gba_state = GBA2_GBA_STATE_ALREADY_EXIST; + break; + } + + default: + { + gba_state = GBA2_GBA_STATE_ERROR; + break; + } + } + } + } + else { + send = 0xFFFE0109; + send = mJOYSIO_ConvData((u8*)&send); + + if (GBAWrite(Port, (u8*)&send, &status) != GBA_READY) { + gba_state = GBA2_GBA_STATE_ERROR; + } + + if (GBAReset(Port, &status2) != GBA_READY) { + gba_state = GBA2_GBA_STATE_ERROR; + } + } + } + else if (status & GBA_JSTAT_SEND) { + recv; + if (GBARead(Port, (u8*)&recv, &status2) != GBA_READY) { + gba_state = GBA2_GBA_STATE_ERROR; + } + else{ + switch (mJOYSIO_ConvData((u8*)&recv)) { + case 0xFFFE0208: + { + joywork->scan_state = 1; + break; + } + + case 0xFFFE020A: + { + gba_state = GBA2_GBA_STATE_ALREADY_EXIST; + break; + } + + default: + { + gba_state = GBA2_GBA_STATE_ERROR; + break; + } + } + } + } + else { + send = cmd; + send = mJOYSIO_ConvData((u8*)&send); + + if (GBAWrite(Port, (u8*)&send, &status) != GBA_READY) { + gba_state = GBA2_GBA_STATE_ERROR; + } + + if (GBAReset(Port, &status2) != GBA_READY) { + gba_state = GBA2_GBA_STATE_ERROR; + } + } + } + + return gba_state; +} + +static int mGcgba_IsTypes(Joy_wrk_c* joywork, u32 load_type, u32 program_type) { + int gba_status = GBA2_GBA_STATE_TRANSMITTING; + u8 status; + u32 send; + + if (GBAGetStatus(Port, &status) != GBA_READY) { + gba_status = GBA2_GBA_STATE_ERROR; + } + else { + if (status & GBA_JSTAT_SEND) { + u32 recv; + u8 readStatus; + + if (GBARead(Port, (u8*)&recv, &readStatus) != GBA_READY) { + gba_status = GBA2_GBA_STATE_ERROR; + goto exit; + } + else { + if (joywork->scan_state == 1) { + if (load_type == mJOYSIO_ConvData((u8*)&recv)) { + gba_status = GBA2_GBA_STATE_SUCCESS; + goto exit; + } + + if (program_type == mJOYSIO_ConvData((u8*)&recv)) { + gba_status = GBA2_GBA_STATE_ALREADY_EXIST; + goto exit; + } + + gba_status = GBA2_GBA_STATE_ERROR; + goto exit; + } + + joywork->cmd_attempts = 0; + } + } + + send = 0xFFFE0105; + send = mJOYSIO_ConvData((u8*)&send); + + if (GBAWrite(Port, (u8*)&send, &status) == GBA_READY) { + joywork->scan_state = 1; + } + else { + gba_status = GBA2_GBA_STATE_ERROR; + } + } + + exit: + return gba_status; +} + +extern int mGcgba_IsEditor() { + int gba_state = GBA2_GBA_STATE_TRANSMITTING; + + if (Port < GBA_CHAN0 || Port >= GBA_MAX_CHAN) { + return GBA2_GBA_STATE_ERROR; + } + else { + if (g_Joy_wrk.cmd_attempts < GBA2_DEFAULT_TRIES) { + switch (g_Joy_wrk.cmd_stage) + { + case 0: + { + if (mJOYSIO_OpenConnection(&g_Joy_wrk) == GBA2_GBA_STATE_SUCCESS) { + g_Joy_wrk.cmd_stage++; + } + break; + } + + case 1: + { + int attempt; + + for (attempt = 0; attempt < 24; attempt++) { + gba_state = mGcgba_IsTypes(&g_Joy_wrk, 0xFFFE0206, 0xFFFE0207); + + if (gba_state != GBA2_GBA_STATE_TRANSMITTING) { + break; + } + } + break; + } + } + } + else { + gba_state = GBA2_GBA_STATE_ERROR; + } + + g_Joy_wrk.cmd_attempts++; + } + + return gba_state; +} + +extern int mGcgba_IsIsland() { + int gba_state = GBA2_GBA_STATE_TRANSMITTING; + + if (Port < GBA_CHAN0 || Port >= GBA_MAX_CHAN) { + return GBA2_GBA_STATE_ERROR; + } + else { + if (g_Joy_wrk.cmd_attempts < GBA2_DEFAULT_TRIES) { + switch (g_Joy_wrk.cmd_stage) + { + case 0: + { + if (mJOYSIO_OpenConnection(&g_Joy_wrk) == GBA2_GBA_STATE_SUCCESS) { + g_Joy_wrk.cmd_stage++; + } + break; + } + + case 1: + { + int attempt; + + for (attempt = 0; attempt < 24; attempt++) { + gba_state = mGcgba_IsTypes(&g_Joy_wrk, 0xFFFE0207, 0xFFFE0206); + + if (gba_state != GBA2_GBA_STATE_TRANSMITTING) { + break; + } + } + break; + } + } + } + else { + gba_state = GBA2_GBA_STATE_ERROR; + } + + g_Joy_wrk.cmd_attempts++; + } + + return gba_state; +} + +static int mGcgba_Recv_sub(Joy_wrk_c* jwork, u8* recv_buf, size_t buf_size, u32 cmd) { + int gba_state = GBA2_GBA_STATE_TRANSMITTING; + u8 status; + u8 read_status; + u32 gba_cmd; + u32 recv; + u32 data; + + if (GBAGetStatus(Port, &status) != GBA_READY) { + gba_state = GBA2_GBA_STATE_ERROR; + } + else { + if (status & GBA_JSTAT_SEND) { + + if (GBARead(Port, (u8*)&recv, &read_status) != GBA_READY) { + gba_state = GBA2_GBA_STATE_ERROR; + } + else { + data = mJOYSIO_ConvData((u8*)&recv); + switch (jwork->recv_state) { + case 0: + { + if (data == 0xFFFE0204) { + jwork->readwrite_ofs = 0; + jwork->packets_processed = 0; + jwork->packet_block_checksum = 0; + jwork->total_xfer_checksum = 0; + jwork->checksum_valid = FALSE; + jwork->checksum_invalid = FALSE; + jwork->recv_state++; + } + break; + } + + case 1: + { + if (jwork->readwrite_ofs == (int)buf_size / 4) { + jwork->total_xfer_checksum += data; + if (jwork->total_xfer_checksum == 0xFFFFFFFF) { + jwork->checksum_valid = TRUE; + } + else { + jwork->checksum_invalid = TRUE; + } + + jwork->recv_state++; + } + else if ((jwork->packets_processed & 0xFF) == 0xFF) { + jwork->packet_block_checksum += data; + + if (jwork->packet_block_checksum != 0xFFFFFFFF) { + gba_state = GBA2_GBA_STATE_ERROR; + } + + jwork->packet_block_checksum = 0; + } + else { + memcpy(recv_buf + jwork->readwrite_ofs * sizeof(u32), (u8*)&recv, sizeof(u32)); + jwork->packet_block_checksum += data; + jwork->total_xfer_checksum += data; + jwork->readwrite_ofs++; + } + + jwork->packets_processed++; + jwork->cmd_attempts = 0; + break; + } + + case 2: + { + if (data == 0xFFFE0208) { + gba_state = GBA2_GBA_STATE_SUCCESS; + } + else if (data == 0xFFFE0209) { + gba_state = GBA2_GBA_STATE_ERROR; + } + + break; + } + } + } + } + } + + if (gba_state == GBA2_GBA_STATE_TRANSMITTING) { + if (jwork->checksum_valid == TRUE) { + gba_cmd = 0xFFFE0106; + } + else { + + if (jwork->checksum_invalid == TRUE) { + gba_cmd = 0xFFFE0107; + } + else { + gba_cmd = cmd; + } + } + + gba_cmd = mJOYSIO_ConvData((u8*)&gba_cmd); + if (GBAWrite(Port, (u8*)&gba_cmd, &read_status) != GBA_READY) { + gba_state = GBA2_GBA_STATE_ERROR; + } + } + + return gba_state; +} + +extern int mGcgba_Recv(u8* recv_bufp, size_t recv_buf_size) { + int gba_state = GBA2_GBA_STATE_TRANSMITTING; + + if (Port < GBA_CHAN0 || Port >= GBA_MAX_CHAN) { + return GBA2_GBA_STATE_ERROR; + } + else { + if (g_Joy_wrk.cmd_attempts < GBA2_DEFAULT_TRIES) { + switch (g_Joy_wrk.cmd_stage) + { + case 0: + { + if (mJOYSIO_OpenConnection(&g_Joy_wrk) == GBA2_GBA_STATE_SUCCESS) { + g_Joy_wrk.cmd_stage++; + } + break; + } + + case 1: + { + int attempt; + + for (attempt = 0; attempt < 24; attempt++) { + gba_state = mGcgba_Recv_sub(&g_Joy_wrk, recv_bufp, recv_buf_size, 0xFFFE0102); + + if (gba_state != GBA2_GBA_STATE_TRANSMITTING) { + break; + } + } + break; + } + } + } + else { + gba_state = GBA2_GBA_STATE_ERROR; + } + + g_Joy_wrk.cmd_attempts++; + } + + return gba_state; +} + +extern int mGcgba_Read(u8* read_buf, size_t read_buf_size, u8 prog_type) { + int gba_state = GBA2_GBA_STATE_TRANSMITTING; + + if (Port < GBA_CHAN0 || Port >= GBA_MAX_CHAN) { + printf("port 設定されていない\n"); // port not set + return GBA2_GBA_STATE_ERROR; + } + else { + if ((g_Joy_wrk.cmd_stage == 1 && g_Joy_wrk.cmd_attempts < GBA2_READ_TRIES) || + (g_Joy_wrk.cmd_stage != 1 && g_Joy_wrk.cmd_attempts < GBA2_DEFAULT_TRIES) + ) { + switch (g_Joy_wrk.cmd_stage) { + case 0: + { + printf("read:イニシャルコード交換\n"); // read: initial code exchange + + if (mJOYSIO_OpenConnection(&g_Joy_wrk) == GBA2_GBA_STATE_SUCCESS) { + g_Joy_wrk.cmd_stage++; + } + break; + } + + case 1: + { + int res; + printf("read:スキャン\n"); // read: scan + + switch (prog_type) { + case GBA2_EAPPLI_TYPE_NEEDLEWORK: + { + res = mGcgba_StartScan(&g_Joy_wrk, 0xFFFE0108); + break; + } + + case GBA2_EAPPLI_TYPE_MUSICSCORE: + { + res = mGcgba_StartScan(&g_Joy_wrk, 0xFFFE010B); + break; + } + + default: /* GBA2_EAPPLI_TYPE_PTERMINAL */ + { + res = mGcgba_StartScan(&g_Joy_wrk, 0xFFFE010C); + break; + } + } + + if (res == GBA2_GBA_STATE_SUCCESS) { + g_Joy_wrk.cmd_stage++; + g_Joy_wrk.cmd_attempts = 0; + } + else if (res == GBA2_GBA_STATE_ERROR) { + gba_state = GBA2_GBA_STATE_ERROR; + } + break; + } + + case 2: + { + int attempt; + for (attempt = 0; attempt < 24; attempt++) { + gba_state = mGcgba_Recv_sub(&g_Joy_wrk, read_buf, read_buf_size, 0xFFFE010A); + if (gba_state != GBA2_GBA_STATE_TRANSMITTING) { + break; + } + } + + break; + } + } + } + else { + gba_state = GBA2_GBA_STATE_ERROR; + } + + g_Joy_wrk.cmd_attempts++; + } + + return gba_state; +} + +extern int mGcgba_CheckRecv(u8* recv_bufp, size_t recv_buf_size) { + int gba_state = GBA2_GBA_STATE_TRANSMITTING; + + if (Port < GBA_CHAN0 || Port >= GBA_MAX_CHAN) { + return GBA2_GBA_STATE_ERROR; + } + else { + if (g_Joy_wrk.cmd_attempts < GBA2_DEFAULT_TRIES) { + switch (g_Joy_wrk.cmd_stage) + { + case 0: + { + if (mJOYSIO_OpenConnection(&g_Joy_wrk) == GBA2_GBA_STATE_SUCCESS) { + g_Joy_wrk.cmd_stage++; + } + break; + } + + case 1: + { + int attempt; + + for (attempt = 0; attempt < 24; attempt++) { + gba_state = mGcgba_Recv_sub(&g_Joy_wrk, recv_bufp, recv_buf_size, 0xFFFE0101); + + if (gba_state != GBA2_GBA_STATE_TRANSMITTING) { + break; + } + } + break; + } + } + } + else { + gba_state = GBA2_GBA_STATE_ERROR; + } + + g_Joy_wrk.cmd_attempts++; + } + + return gba_state; +} + +static int mGcgba_Send_sub(Joy_wrk_c* jwork, u8* send_bufp, size_t send_buf_size) { + int gba_state = GBA2_GBA_STATE_TRANSMITTING; + u8 status; + u8 read_status; + u8 received_data; + u8 sent_data; + int recv_cmd; + u32 send_cmd; + u32 recv; + + if (GBAGetStatus(Port, &status) != GBA_READY) { + gba_state = GBA2_GBA_STATE_ERROR; + } + else { + received_data = FALSE; + sent_data = FALSE; + jwork->status = status; + + if (status & GBA_JSTAT_SEND) { + if (GBARead(Port, (u8*)&recv, &read_status) != GBA_READY) { + gba_state = GBA2_GBA_STATE_ERROR; + goto exit; + } + + recv_cmd = mJOYSIO_ConvData((u8*)&recv); + received_data = TRUE; + } + + switch (jwork->send_state) { + case 0: + { + if (received_data == TRUE && recv_cmd == 0xFFFE0104) { + jwork->readwrite_ofs = 0; + jwork->packets_processed = 0; + jwork->packet_block_checksum = 0; + jwork->total_xfer_checksum = 0; + jwork->send_state++; + } + break; + } + + case 1: + { + if ((status & GBA_JSTAT_RECV) == 0) { + if (jwork->readwrite_ofs == (int)send_buf_size / 4) { + send_cmd = ~jwork->total_xfer_checksum; + jwork->send_state++; + } + else if ((jwork->packets_processed & 0xFF) == 0xFF) { + send_cmd = ~jwork->packet_block_checksum; + jwork->packet_block_checksum = 0; + } + else { + send_cmd = *(u32*)(send_bufp + jwork->readwrite_ofs * sizeof(u32)); + jwork->packet_block_checksum += send_cmd; + jwork->total_xfer_checksum += send_cmd; + jwork->readwrite_ofs++; + } + + if (GBAWrite(Port, (u8*)&send_cmd, &read_status) != GBA_READY) { + gba_state = GBA2_GBA_STATE_ERROR; + } + + sent_data = TRUE; + jwork->packets_processed++; + jwork->cmd_attempts = 0; + } + else { + if (GBAReset(Port, &read_status) != GBA_READY) { + gba_state = GBA2_GBA_STATE_ERROR; + } + } + break; + } + + case 2: + { + if (received_data == TRUE) { + if (recv_cmd == 0xFFFE0208) { + gba_state = GBA2_GBA_STATE_SUCCESS; + } + else if (recv_cmd == 0xFFFE0209) { + gba_state = GBA2_GBA_STATE_ERROR; + } + } + + break; + } + } + } + + exit: + if (gba_state == GBA2_GBA_STATE_TRANSMITTING && jwork->send_state != 1 && sent_data == FALSE) { + send_cmd = 0xFFFE0202; + send_cmd = mJOYSIO_ConvData((u8*)&send_cmd); + + if (GBAWrite(Port, (u8*)&send_cmd, &read_status) != GBA_READY) { + gba_state = GBA2_GBA_STATE_ERROR; + } + } + + return gba_state; +} + +extern int mGcgba_Send(u8* send_bufp, size_t send_buf_size) { + int gba_state = GBA2_GBA_STATE_TRANSMITTING; + + if (Port < GBA_CHAN0 || Port >= GBA_MAX_CHAN) { + return GBA2_GBA_STATE_ERROR; + } + else { + if (g_Joy_wrk.cmd_attempts < GBA2_DEFAULT_TRIES) { + switch (g_Joy_wrk.cmd_stage) + { + case 0: + { + if (mJOYSIO_OpenConnection(&g_Joy_wrk) == GBA2_GBA_STATE_SUCCESS) { + g_Joy_wrk.cmd_stage++; + } + break; + } + + case 1: + { + int attempt; + + for (attempt = 0; attempt < 24; attempt++) { + gba_state = mGcgba_Send_sub(&g_Joy_wrk, send_bufp, send_buf_size); + + if (gba_state != GBA2_GBA_STATE_TRANSMITTING) { + break; + } + } + break; + } + } + } + else { + gba_state = GBA2_GBA_STATE_ERROR; + } + + g_Joy_wrk.cmd_attempts++; + } + + return gba_state; +} + +static u32 changeEndian(u32 v) { + return ((v >> 24) & 0xFF) | + ((v >> 8) & 0xFF00) | + ((v & 0xFF00) << 8) | + ((v & 0xFF) << 24); +} + +extern int mGcgba_send_eAppri(u8* eappli_p, size_t eappli_size) { + int gba_state = GBA2_EAPPLI_TRANSMITTING; + int gba_status; + u32 data; + u32 recv; + u8 status; + + if (Port < GBA_CHAN0 || Port >= GBA_MAX_CHAN) { + gba_state = GBA2_EAPPLI_FAILURE_NO_GBA; + } + else { + g_Joy_wrk.cmd_attempts++; + + if (g_Joy_wrk.cmd_attempts < GBA2_DEFAULT_TRIES) { + + gba_status = GBAGetStatus(Port, &status); + if (gba_status == GBA_READY) { + if ((status & GBA_JSTAT_RECV) == 0) { + g_Joy_wrk.recv_wait = 0; + } + else if (g_Joy_wrk.recv_wait < GBA2_SEND_RECV_TRIES) { + g_Joy_wrk.recv_wait++; + } + } + + if (gba_status == GBA_READY && g_Joy_wrk.recv_wait <= GBA2_SEND_RECV_TRIES - 1) { + switch (g_Joy_wrk.cmd_stage) { + case SEND_EAPPLI_STATE_WAIT_FOR_GBA: + { + if ((status & GBA_JSTAT_SEND) != 0 && + (status & GBA_JSTAT_PSF1) != 0 && + (GBARead(Port, (u8*)&recv, &status) == GBA_READY) && + (*(u32*)"PSAE" == recv) + ) { + g_Joy_wrk.cmd_attempts = 0; + g_Joy_wrk.cmd_stage++; + } + else { + if (GBAReset(Port, &status) != GBA_READY) { + gba_state = GBA2_EAPPLI_FAILURE_NO_GBA; + } + } + break; + } + + case SEND_EAPPLI_STATE_GC_ACKNOWLEDGE_TO_GBA: + { + data = *(u32*)"PSAE"; + if (GBAWrite(Port, (u8*)&data, &status) == GBA_READY) { + g_Joy_wrk.cmd_attempts = 0; + g_Joy_wrk.cmd_stage++; + g_Joy_wrk.cmd_stage++; // lol duplication + } + break; + } + + // case 2 unused + + case SEND_EAPPLI_STATE_GC_SEND_EAPPLI_SIZE_TO_GBA: + { + if ((status & GBA_JSTAT_PSF0) && (status & GBA_JSTAT_PSF1)) { + data = changeEndian(eappli_size); + if (GBAWrite(Port, (u8*)&data, &status) == GBA_READY) { + g_Joy_wrk.cmd_attempts = 0; + g_Joy_wrk.cmd_stage++; + } + } + break; + } + + case SEND_EAPPLI_STATE_WAIT_FOR_GBA_ACKNOWLEDGED_EAPPLI_SIZE: + { + if ((status & GBA_JSTAT_SEND) && GBARead(Port, (u8*)&recv, &status) == GBA_READY && eappli_size == changeEndian(recv)) { + g_Joy_wrk.cmd_attempts = 0; + g_Joy_wrk.readwrite_ofs = 0; + g_Joy_wrk.cmd_stage++; + } + break; + } + + case SEND_EAPPLI_STATE_GC_SEND_EAPPLI_DATA_TO_GBA: + { + if ((u32)g_Joy_wrk.readwrite_ofs >= eappli_size / sizeof(u32)) { + g_Joy_wrk.cmd_stage++; + } + else { + if ((status & GBA_JSTAT_FLAGS_MASK) != GBA_JSTAT_FLAGS_MASK) { + return GBA2_EAPPLI_FAILURE_NO_GBA; + } + + if ((status & GBA_JSTAT_RECV) == 0) { + data = *(u32*)(eappli_p + g_Joy_wrk.readwrite_ofs * sizeof(u32)); + + if (GBAWrite(Port, (u8*)&data, &status) == GBA_READY) { + g_Joy_wrk.cmd_attempts = 0; + g_Joy_wrk.readwrite_ofs++; + } + } + } + break; + } + + case SEND_EAPPLI_STATE_GC_OPEN_JOY_SIO_CONNECTION: + { + if (mJOYSIO_OpenConnection(&g_Joy_wrk) == GBA2_GBA_STATE_SUCCESS) { + g_Joy_wrk.cmd_stage++; + } + else { + g_Joy_wrk.cmd_attempts = 0; + } + break; + } + + case SEND_EAPPLI_STATE_JOY_SIO_CONNECTION_OPEN: + { + int attempt; + for (attempt = 0; attempt < 24; attempt++) { + int res = mGcgba_IsTypes(&g_Joy_wrk, 0xFFFE020A, 0xCDCDCDCD); + if (res != GBA2_GBA_STATE_TRANSMITTING) { + if (res == GBA2_GBA_STATE_SUCCESS) { + gba_state = GBA2_EAPPLI_SUCCESS; + } + else { + gba_state = GBA2_EAPPLI_FAILURE_NO_GBA; + } + break; + } + } + break; + } + } + } + else { + gba_state = GBA2_EAPPLI_FAILURE_NO_GBA; + } + } + else { + gba_state = GBA2_EAPPLI_FAILURE_NO_GBA; + } + } + + if (gba_state == GBA2_EAPPLI_FAILURE_NO_GBA && g_Joy_wrk.cmd_stage != 0) { + gba_state = GBA2_EAPPLI_FAILURE_XFER_ERROR; + } + + return gba_state; +}