From 0fbdb1f3818e65a31600e73d906559b895c282bd Mon Sep 17 00:00:00 2001 From: Pwootage Date: Sun, 16 Oct 2022 14:20:36 -0600 Subject: [PATCH] CGBASupport (#13) * Start work on GBASupport * Match update * Almost perfect match * Very close to 100% match * Match & link CGBASupport * Fix some naming * use c-cstle cast and round-up Former-commit-id: d8804b5244e8365aafd58fdf52a67e258e62f4d9 --- asm/MetroidPrime/CFrontEndUI.s | 2 +- asm/MetroidPrime/CGBASupport.s | 22 +-- configure.py | 6 +- include/Kyoto/Alloc/CMemory.hpp | 5 +- include/Kyoto/CDvdFile.hpp | 18 ++- include/MetroidPrime/CGBASupport.hpp | 55 +++++++ include/rstl/rc_ptr.hpp | 1 + obj_files.mk | 40 ++--- src/Dolphin/GBA/GBAWrite.c | 2 +- src/MetroidPrime/CGBASupport.cpp | 217 +++++++++++++++++++++++++++ 10 files changed, 330 insertions(+), 38 deletions(-) create mode 100644 include/MetroidPrime/CGBASupport.hpp create mode 100644 src/MetroidPrime/CGBASupport.cpp diff --git a/asm/MetroidPrime/CFrontEndUI.s b/asm/MetroidPrime/CFrontEndUI.s index e21d88d6..064270c5 100644 --- a/asm/MetroidPrime/CFrontEndUI.s +++ b/asm/MetroidPrime/CFrontEndUI.s @@ -7734,7 +7734,7 @@ Update__13SGBALinkFrameFf: /* 80022024 0001EF84 93 E1 00 14 */ stw r31, 0x14(r1) /* 80022028 0001EF88 7C 7F 1B 78 */ mr r31, r3 /* 8002202C 0001EF8C 80 63 00 04 */ lwz r3, 4(r3) -/* 80022030 0001EF90 48 22 68 E9 */ bl Update__11CGBASupportFv +/* 80022030 0001EF90 48 22 68 E9 */ bl Update__11CGBASupportFf /* 80022034 0001EF94 FC 20 F8 90 */ fmr f1, f31 /* 80022038 0001EF98 80 7F 00 08 */ lwz r3, 8(r31) /* 8002203C 0001EF9C 48 2A 06 C9 */ bl Update__9CGuiFrameFf diff --git a/asm/MetroidPrime/CGBASupport.s b/asm/MetroidPrime/CGBASupport.s index 562e7f09..f700bdf3 100644 --- a/asm/MetroidPrime/CGBASupport.s +++ b/asm/MetroidPrime/CGBASupport.s @@ -32,8 +32,8 @@ lbl_805A82BC: .section .sbss, "wa" .balign 8 -.global lbl_805A9110 -lbl_805A9110: +.global g_GBA +g_GBA: .skip 0x8 .section .text, "ax" @@ -545,8 +545,8 @@ lbl_802488F8: .endif -.global Update__11CGBASupportFv -Update__11CGBASupportFv: +.global Update__11CGBASupportFf +Update__11CGBASupportFf: /* 80248918 00245878 94 21 FF D0 */ stwu r1, -0x30(r1) /* 8024891C 0024587C 7C 08 02 A6 */ mflr r0 /* 80248920 00245880 90 01 00 34 */ stw r0, 0x34(r1) @@ -671,9 +671,9 @@ lbl_80248A5C: /* 80248A7C 002459DC 48 00 00 D8 */ b Update_default Update_case4: /* 80248A80 002459E0 38 00 00 05 */ li r0, 5 -/* 80248A84 002459E4 3C 60 80 25 */ lis r3, nullsub_130@ha +/* 80248A84 002459E4 3C 60 80 25 */ lis r3, joyboot_callback__Fll@ha /* 80248A88 002459E8 90 1F 00 34 */ stw r0, 0x34(r31) -/* 80248A8C 002459EC 39 23 8D D8 */ addi r9, r3, nullsub_130@l +/* 80248A8C 002459EC 39 23 8D D8 */ addi r9, r3, joyboot_callback__Fll@l /* 80248A90 002459F0 39 1F 00 3C */ addi r8, r31, 0x3c /* 80248A94 002459F4 38 A0 00 02 */ li r5, 2 /* 80248A98 002459F8 80 7F 00 40 */ lwz r3, 0x40(r31) @@ -904,7 +904,7 @@ __dt__11CGBASupportFv: /* 80248C98 00245BF8 41 82 00 60 */ beq lbl_80248CF8 /* 80248C9C 00245BFC 38 60 00 00 */ li r3, 0 /* 80248CA0 00245C00 34 1E 00 30 */ addic. r0, r30, 0x30 -/* 80248CA4 00245C04 90 6D A5 50 */ stw r3, lbl_805A9110@sda21(r13) +/* 80248CA4 00245C04 90 6D A5 50 */ stw r3, g_GBA@sda21(r13) /* 80248CA8 00245C08 41 82 00 24 */ beq lbl_80248CCC /* 80248CAC 00245C0C 80 7E 00 30 */ lwz r3, 0x30(r30) /* 80248CB0 00245C10 28 03 00 00 */ cmplwi r3, 0 @@ -992,7 +992,7 @@ lbl_80248E38: /* 80248EB8 00245E18 98 7F 00 44 */ stb r3, 0x44(r31) /* 80248EBC 00245E1C 98 7F 00 45 */ stb r3, 0x45(r31) /* 80248EC0 00245E20 48 18 08 91 */ bl GBAInit -/* 80248EC4 00245E24 93 ED A5 50 */ stw r31, lbl_805A9110@sda21(r13) +/* 80248EC4 00245E24 93 ED A5 50 */ stw r31, g_GBA@sda21(r13) /* 80248EC8 00245E28 48 13 81 ED */ bl OSGetFontEncode /* 80248ECC 00245E2C 80 01 00 24 */ lwz r0, 0x24(r1) /* 80248ED0 00245E30 7F E3 FB 78 */ mr r3, r31 @@ -1047,7 +1047,7 @@ __ct__11CGBASupportFv: /* 80248DB0 00245D10 98 7F 00 44 */ stb r3, 0x44(r31) /* 80248DB4 00245D14 98 7F 00 45 */ stb r3, 0x45(r31) /* 80248DB8 00245D18 48 18 08 95 */ bl GBAInit -/* 80248DBC 00245D1C 93 ED A5 50 */ stw r31, lbl_805A9110@sda21(r13) +/* 80248DBC 00245D1C 93 ED A5 50 */ stw r31, g_GBA@sda21(r13) /* 80248DC0 00245D20 7F E3 FB 78 */ mr r3, r31 /* 80248DC4 00245D24 80 01 00 24 */ lwz r0, 0x24(r1) /* 80248DC8 00245D28 83 E1 00 1C */ lwz r31, 0x1c(r1) @@ -1057,8 +1057,8 @@ __ct__11CGBASupportFv: .endif -.global nullsub_130 -nullsub_130: +.global joyboot_callback__Fll +joyboot_callback__Fll: /* 80248DD8 00245D38 4E 80 00 20 */ blr .section .sdata2, "a" diff --git a/configure.py b/configure.py index 61db81b4..3da98448 100755 --- a/configure.py +++ b/configure.py @@ -12,7 +12,7 @@ parser.add_argument('--version', dest='version', default='0', help='version to build (0, 1, kor)') parser.add_argument('--map', dest='map', action='store_true', help='generate map file') -parser.add_argument('--no-check', dest='check', action='store_false', +parser.add_argument('--no-check', dest='check', action='store_false', help='don\'t check hash of resulting dol') parser.add_argument('--static-libs', dest='static_libs', action='store_true', help='build and use static libs') @@ -343,7 +343,7 @@ LIBS = [ "MetroidPrime/CPauseScreenFrame", "MetroidPrime/Enemies/CAtomicAlpha", "MetroidPrime/CLogBookScreen", - "MetroidPrime/CGBASupport", + ["MetroidPrime/CGBASupport", True], "MetroidPrime/Player/CSaveWorld", "MetroidPrime/ScriptObjects/CScriptCameraHintTrigger", "MetroidPrime/Enemies/CAmbientAI", @@ -1193,7 +1193,7 @@ for lib in LIBS: else: for object in lib["objects"]: completed = False - + if type(object) is list: completed = object[1] object = object[0] diff --git a/include/Kyoto/Alloc/CMemory.hpp b/include/Kyoto/Alloc/CMemory.hpp index 5941b8a2..c1988105 100644 --- a/include/Kyoto/Alloc/CMemory.hpp +++ b/include/Kyoto/Alloc/CMemory.hpp @@ -3,6 +3,7 @@ #include "types.h" #include "Kyoto/Alloc/IAllocator.hpp" +#include "Kyoto/Alloc/CCallStack.hpp" class COsContext; class CMemory { @@ -14,7 +15,9 @@ public: static void Shutdown(); static void SetAllocator(COsContext& ctx, IAllocator& allocator); static IAllocator* GetAllocator(); - static void* Alloc(size_t len, IAllocator::EHint hint, IAllocator::EScope scope, IAllocator::EType type, const CCallStack& callstack); + static void* Alloc(size_t len, IAllocator::EHint hint = IAllocator::kHI_None, + IAllocator::EScope scope = IAllocator::kSC_Unk1, IAllocator::EType type = IAllocator::kTP_Heap, + const CCallStack& callstack = CCallStack(-1, "??(??)")); static void Free(const void* ptr); static void SetOutOfMemoryCallback(IAllocator::FOutOfMemoryCb callback, const void* context); static void OffsetFakeStatics(int); diff --git a/include/Kyoto/CDvdFile.hpp b/include/Kyoto/CDvdFile.hpp index f19c8870..c68b52ea 100644 --- a/include/Kyoto/CDvdFile.hpp +++ b/include/Kyoto/CDvdFile.hpp @@ -3,12 +3,28 @@ #include "types.h" +class IDvdRequest { +public: + virtual ~IDvdRequest() = 0; // 8 + virtual void Unknown1(bool) = 0; // c + virtual bool IsComplete() = 0; // 10 +}; + class CDvdFile { public: + CDvdFile(const char* name); + ~CDvdFile(); + uint Length() { return x14_size; } + + IDvdRequest* SyncRead(void* buf, uint len); + static bool FileExists(const char*); private: - uchar pad[0x28]; + uchar pad[0x14]; + uint x14_size; + uchar pad2[0x10]; }; +CHECK_SIZEOF(CDvdFile, 0x28) #endif // _CDVDFILE diff --git a/include/MetroidPrime/CGBASupport.hpp b/include/MetroidPrime/CGBASupport.hpp new file mode 100644 index 00000000..69e6354c --- /dev/null +++ b/include/MetroidPrime/CGBASupport.hpp @@ -0,0 +1,55 @@ +#ifndef _CGBASUPPORT +#define _CGBASUPPORT + +#include "Kyoto/CDvdFile.hpp" +#include "rstl/auto_ptr.hpp" +#include "rstl/single_ptr.hpp" +#include "rstl/rc_ptr.hpp" + +class CGBASupport { +public: + enum EPhase { + kP_LoadClientPad, + kP_Standby, + kP_StartProbeTimeout, + kP_PollProbe, + kP_StartJoyBusBoot, + kP_PollJoyBusBoot, + kP_DataTransfer, + kP_Complete, + kP_Failed + }; + +private: + CDvdFile x0_file; + uint x28_fileSize; + rstl::single_ptr x2c_buffer; + rstl::single_ptr x30_dvdReq; + EPhase x34_phase; + float x38_timeout; + uchar x3c_status; + uint x40_siChan; + bool x44_fusionLinked; + bool x45_fusionBeat; + +// this is inlined weirdly... this probably should be handled differently + bool CheckReadyStatus(); +public: + static void Initialize(); + static void GlobalPoll(); + + CGBASupport(); + ~CGBASupport(); + bool PollResponse(); + void Update(float dt); + bool IsReady(); + void InitializeSupport(); + void StartLink(); + EPhase GetPhase() const { return x34_phase; } + bool IsFusionLinked() const { return x44_fusionLinked; } + bool IsFusionBeat() const { return x45_fusionBeat; } +}; + +extern CGBASupport* g_GBA; + +#endif // _CGBASUPPORT diff --git a/include/rstl/rc_ptr.hpp b/include/rstl/rc_ptr.hpp index 28f08259..53584dee 100644 --- a/include/rstl/rc_ptr.hpp +++ b/include/rstl/rc_ptr.hpp @@ -35,6 +35,7 @@ public: } T* operator->() const { return GetPtr(); } T& operator*() const { return *GetPtr(); } + operator bool() const { return GetPtr() != nullptr; } private: CRefData* x0_refData; diff --git a/obj_files.mk b/obj_files.mk index 1596e901..51135354 100644 --- a/obj_files.mk +++ b/obj_files.mk @@ -1,6 +1,6 @@ METROTRK_FILES :=\ $(BUILD_DIR)/src/MetroTRK/mslsupp.o - + METROIDPRIME :=\ $(BUILD_DIR)/asm/MetroidPrime/main.o\ $(BUILD_DIR)/asm/MetroidPrime/IRenderer.o\ @@ -310,7 +310,7 @@ METROIDPRIME :=\ $(BUILD_DIR)/asm/MetroidPrime/CPauseScreenFrame.o\ $(BUILD_DIR)/asm/MetroidPrime/Enemies/CAtomicAlpha.o\ $(BUILD_DIR)/asm/MetroidPrime/CLogBookScreen.o\ - $(BUILD_DIR)/asm/MetroidPrime/CGBASupport.o\ + $(BUILD_DIR)/src/MetroidPrime/CGBASupport.o\ $(BUILD_DIR)/asm/MetroidPrime/Player/CSaveWorld.o\ $(BUILD_DIR)/asm/MetroidPrime/ScriptObjects/CScriptCameraHintTrigger.o\ $(BUILD_DIR)/asm/MetroidPrime/Enemies/CAmbientAI.o\ @@ -370,7 +370,7 @@ WORLDFORMAT :=\ $(BUILD_DIR)/asm/WorldFormat/CCollidableOBBTreeGroup.o\ $(BUILD_DIR)/asm/WorldFormat/CPVSAreaSet.o\ $(BUILD_DIR)/asm/WorldFormat/CAreaRenderOctTree.o\ - + WEAPONS :=\ $(BUILD_DIR)/asm/Weapons/CProjectileWeapon.o\ $(BUILD_DIR)/asm/Weapons/CProjectileWeaponDataFactory.o\ @@ -380,10 +380,10 @@ WEAPONS :=\ $(BUILD_DIR)/asm/Weapons/CDecal.o\ $(BUILD_DIR)/asm/Weapons/CWeaponDescription.o\ $(BUILD_DIR)/asm/Weapons/CDecalDescription.o\ - + METARENDER :=\ $(BUILD_DIR)/asm/MetaRender/CCubeRenderer.o\ - + GUISYS :=\ $(BUILD_DIR)/asm/GuiSys/CAuiMain.o\ $(BUILD_DIR)/asm/GuiSys/CAuiMeter.o\ @@ -631,25 +631,25 @@ KYOTO_2 :=\ $(BUILD_DIR)/src/Kyoto/Audio/g721.o\ $(BUILD_DIR)/asm/Kyoto/Audio/CStaticAudioPlayer.o\ $(BUILD_DIR)/asm/Kyoto/CFrameDelayedKiller.o\ - + AI_FILES :=\ $(BUILD_DIR)/asm/Dolphin/ai.o\ - + AR_FILES :=\ $(BUILD_DIR)/asm/Dolphin/ar/ar.o\ $(BUILD_DIR)/asm/Dolphin/ar/arq.o - + BASE_FILES :=\ $(BUILD_DIR)/src/Dolphin/PPCArch.o\ - + DB_FILES :=\ $(BUILD_DIR)/src/Dolphin/db.ep.o\ - + DSP_FILES :=\ $(BUILD_DIR)/asm/Dolphin/dsp/dsp.o\ $(BUILD_DIR)/src/Dolphin/dsp/dsp_debug.o\ $(BUILD_DIR)/asm/Dolphin/dsp/dsp_task.o - + DVD_FILES :=\ $(BUILD_DIR)/asm/Dolphin/dvd/dvdlow.o\ $(BUILD_DIR)/asm/Dolphin/dvd/dvdfs.o\ @@ -659,7 +659,7 @@ DVD_FILES :=\ $(BUILD_DIR)/asm/Dolphin/dvd/dvdidutils.o\ $(BUILD_DIR)/asm/Dolphin/dvd/dvdfatal.o\ $(BUILD_DIR)/asm/Dolphin/dvd/fstload.o - + GX_FILES :=\ $(BUILD_DIR)/asm/Dolphin/gx/GXInit.o\ $(BUILD_DIR)/asm/Dolphin/gx/GXFifo.o\ @@ -711,10 +711,10 @@ OS_FILES :=\ PAD_FILES :=\ $(BUILD_DIR)/src/Dolphin/pad/PadClamp.ep.o\ $(BUILD_DIR)/asm/Dolphin/pad/pad.o - + VI_FILES :=\ $(BUILD_DIR)/asm/Dolphin/vi.o - + MSL_PPCEABI_BARE_H :=\ $(BUILD_DIR)/src/Runtime/__mem.o\ $(BUILD_DIR)/src/Runtime/__va_arg.o\ @@ -782,7 +782,7 @@ MSL_COMMON_MATH :=\ $(BUILD_DIR)/src/Runtime/w_log.o\ $(BUILD_DIR)/src/Runtime/w_pow.o\ $(BUILD_DIR)/src/Runtime/math_ppc.o\ - + MUSYX_FILES :=\ $(BUILD_DIR)/asm/musyx/seq.o\ $(BUILD_DIR)/asm/musyx/synth.o\ @@ -815,10 +815,10 @@ MUSYX_FILES :=\ $(BUILD_DIR)/asm/musyx/reverb.o\ $(BUILD_DIR)/src/musyx/delay_fx.o\ $(BUILD_DIR)/asm/musyx/chorus_fx.o\ - + DTK_FILES :=\ $(BUILD_DIR)/src/Dolphin/dtk.o - + CARD_FILES :=\ $(BUILD_DIR)/asm/Dolphin/card/CARDBios.o\ $(BUILD_DIR)/src/Dolphin/card/CARDUnlock.ep.o\ @@ -836,11 +836,11 @@ CARD_FILES :=\ $(BUILD_DIR)/src/Dolphin/card/CARDStat.ep.o\ $(BUILD_DIR)/src/Dolphin/card/CARDRename.ep.o\ $(BUILD_DIR)/src/Dolphin/card/CARDNet.ep.o - + SI_FILES :=\ $(BUILD_DIR)/asm/Dolphin/si/SIBios.o\ $(BUILD_DIR)/src/Dolphin/si/SISamplingRate.ep.o - + EXI_FILES :=\ $(BUILD_DIR)/asm/Dolphin/exi/EXIBios.o\ $(BUILD_DIR)/asm/Dolphin/exi/EXIUart.o @@ -848,7 +848,7 @@ EXI_FILES :=\ THP_FILES :=\ $(BUILD_DIR)/asm/Dolphin/thp/THPDec.o\ $(BUILD_DIR)/asm/Dolphin/thp/THPAudio.o - + GBA_FILES :=\ $(BUILD_DIR)/src/Dolphin/GBA/GBA.ep.o\ $(BUILD_DIR)/asm/Dolphin/GBA/GBAGetProcessStatus.o\ diff --git a/src/Dolphin/GBA/GBAWrite.c b/src/Dolphin/GBA/GBAWrite.c index ae8ecebe..f040f922 100644 --- a/src/Dolphin/GBA/GBAWrite.c +++ b/src/Dolphin/GBA/GBAWrite.c @@ -12,7 +12,7 @@ void WriteProc(s32 chan) { } s32 GBAWriteAsync(s32 chan, u8* src, u8* status, GBACallback callback) { - GBA* gba; + GBA* gba; s32 ret; gba = &__GBA[chan]; diff --git a/src/MetroidPrime/CGBASupport.cpp b/src/MetroidPrime/CGBASupport.cpp new file mode 100644 index 00000000..f9127718 --- /dev/null +++ b/src/MetroidPrime/CGBASupport.cpp @@ -0,0 +1,217 @@ +#include "MetroidPrime/CGBASupport.hpp" +#include "Kyoto/Alloc/CCallStack.hpp" +#include "Kyoto/Alloc/CMemory.hpp" +#include "Kyoto/Basics/CBasics.hpp" +#include "dolphin/gba.h" +#include "dolphin/os.h" +#include "dolphin/os/OSSerial.h" +#include "rstl/math.hpp" + +void joyboot_callback(s32 chan, s32 ret) {} + +const uint MAGIC = 0x414d5445; + +static CGBASupport* g_GBA; + +CGBASupport::CGBASupport() +: x0_file("client_pad.bin") +, x28_fileSize(OSRoundUp32B(x0_file.Length())) +, x2c_buffer((uchar*)CMemory::Alloc(x28_fileSize, IAllocator::kHI_RoundUpLen)) +, x30_dvdReq(x0_file.SyncRead(x2c_buffer.get(), x28_fileSize)) +, x34_phase(kP_LoadClientPad) +, x38_timeout(0.f) +, x3c_status(0) +, x40_siChan(-1) +, x44_fusionLinked(false) +, x45_fusionBeat(false) { + GBAInit(); + g_GBA = this; +} + +CGBASupport::~CGBASupport() { g_GBA = nullptr; } + +void CGBASupport::InitializeSupport() { + x34_phase = kP_Standby; + x38_timeout = 0.f; + x3c_status = 0; + x40_siChan = -1; + x44_fusionLinked = false; + x45_fusionBeat = false; +} + +void CGBASupport::StartLink() { + x34_phase = kP_StartProbeTimeout; + x40_siChan = -1; +} + +inline bool CGBASupport::CheckReadyStatus() { + if (x34_phase != kP_LoadClientPad) + return true; + if (x30_dvdReq->IsComplete()) { + x30_dvdReq = nullptr; + x34_phase = kP_Standby; + uchar* buff = x2c_buffer.get(); + u32 tick = OSGetTick(); + buff[0xc8] = (tick >> 0); + buff[0xc9] = (tick >> 8); + buff[0xca] = (tick >> 16); + buff[0xcb] = (tick >> 24); + buff[0xaf] = 'E'; // set region to 'E' instead of 'J' + buff[0xbd] = 0xc9; + return true; + } + return false; +} + +bool CGBASupport::IsReady() { return CheckReadyStatus(); } + +void CGBASupport::Update(float dt) { + switch (x34_phase) { + case kP_LoadClientPad: + CheckReadyStatus(); + break; + + case kP_StartProbeTimeout: + x38_timeout = 4.f; + x34_phase = kP_PollProbe; + // [[fallthrough]]; + + case kP_PollProbe: + int channel = 1; + do { + uint result = SIProbe(channel); + if (result == 0x40000) { + x40_siChan = channel; + x34_phase = kP_StartJoyBusBoot; + x38_timeout = 4.f; + goto end_switch; + } + channel++; + } while (channel < 4); + float newT = rstl::max_val(0.f, x38_timeout - dt); + x38_timeout = newT; + if (x38_timeout == 0.f) { + x34_phase = kP_Failed; + } + break; + + case kP_StartJoyBusBoot: + x34_phase = kP_PollJoyBusBoot; + GBAJoyBootAsync(x40_siChan, x40_siChan << 1, 2, x2c_buffer.get(), x0_file.Length(), + &x3c_status, &joyboot_callback); + break; + + case kP_PollJoyBusBoot: + int status = GBAGetProcessStatus(x40_siChan, &x3c_status); + if (status != GBA_BUSY) { + if (GBAGetStatus(x40_siChan, &x3c_status) == GBA_NOT_READY) { + x34_phase = kP_Failed; + } else { + x38_timeout = 4.f; + x34_phase = kP_DataTransfer; + } + } + break; + case kP_DataTransfer: + if (PollResponse()) { + x34_phase = kP_Complete; + break; + } + x38_timeout = rstl::max_val(0.f, x38_timeout - dt); + if (x38_timeout == 0.f) + x34_phase = kP_Failed; + break; + case kP_Complete: + case kP_Failed: + break; + } +end_switch:; +} + +inline uchar CalculateFusionJBusChecksum(const uchar* data, uint i) { + // const uchar* data = reinterpret_cast< const uchar* >(dataPtr); + // uint i = 3; + uint sum = -1; + do { + uchar ch = *data++; + sum ^= ch; + for (int j = 0; j < 8; ++j) { + if ((sum & 1)) { + sum >>= 1; + sum ^= 0xb010; + } else + sum >>= 1; + } + } while (--i); + return sum; +} + +bool CGBASupport::PollResponse() { + uchar gbaStatus; + uint unk; + + // Not sure why this is called twice + if (GBAReset(x40_siChan, &gbaStatus) == GBA_NOT_READY && + GBAReset(x40_siChan, &gbaStatus) == GBA_NOT_READY) { + return false; + } + if (GBAGetStatus(x40_siChan, &gbaStatus) == GBA_NOT_READY) { + return false; + } + if (gbaStatus != 0x28) { + return false; + } + uint magic; + if (GBARead(x40_siChan, reinterpret_cast(&magic), &gbaStatus) == GBA_NOT_READY) { + return false; + } + if (magic != 0x414d5445) { // "AMTE" + return false; + } + if (GBAGetStatus(x40_siChan, &gbaStatus) == GBA_NOT_READY) { + return false; + } + if (gbaStatus != 0x20) { + return false; + } + if (GBAWrite(x40_siChan, (u8*)(&MAGIC), &gbaStatus) == GBA_NOT_READY) { + return false; + } + if (GBAGetStatus(x40_siChan, &gbaStatus) == GBA_NOT_READY) { + return false; + } + if ((gbaStatus & 0x30) != 0x30) { + return false; + } + uint start = OSGetTick(); + do { + uint current = OSGetTick(); + if (OSTicksToMicroseconds(current - start) > 500) { + goto end; + } + } while ((GBAGetStatus(x40_siChan, &gbaStatus) == GBA_NOT_READY || (gbaStatus & 0x8) == 0) || + (GBAGetStatus(x40_siChan, &gbaStatus) != GBA_READY || gbaStatus != 0x38)); + + uint read; + uchar fusionStatus[4]; + if (GBARead(x40_siChan, reinterpret_cast(&read), &gbaStatus) != GBA_READY) { + return false; + } + fusionStatus[0] = read >> 24; + fusionStatus[1] = read >> 16; + fusionStatus[2] = read >> 8; + fusionStatus[3] = read; + if (fusionStatus[3] != CalculateFusionJBusChecksum(fusionStatus, 3)) { + return false; + } + + x44_fusionLinked = (fusionStatus[2] & 0x2) == 0; + bool fusionBeat = false; + if (x44_fusionLinked != false && (fusionStatus[2] & 0x1) > 0) { + fusionBeat = true; + } + x45_fusionBeat = fusionBeat; + +end: + return true; +}