diff --git a/include/revolution/dvd.h b/include/revolution/dvd.h index 5dc9067cd8e..31f6c1badf6 100644 --- a/include/revolution/dvd.h +++ b/include/revolution/dvd.h @@ -8,6 +8,9 @@ extern "C" { #endif +#define DVD_DEVICE_CODE_READ (1 << 15) +#define MAKE_DVD_DEVICE_CODE(x) (DVD_DEVICE_CODE_READ | (x)) + #define DVD_ASSERTMSGLINE(line, cond, msg) \ if (!(cond)) \ OSPanic(__FILE__, line, msg) diff --git a/include/revolution/os/OSHardware.h b/include/revolution/os/OSHardware.h new file mode 100644 index 00000000000..aa4a651f704 --- /dev/null +++ b/include/revolution/os/OSHardware.h @@ -0,0 +1,267 @@ +/** + * For more details, see: + * https://www.gc-forever.com/yagcd/chap4.html#sec4 + * https://www.gc-forever.com/yagcd/chap13.html#sec13 + * https://wiibrew.org/wiki/Memory_map + */ + +#ifndef _RVL_SDK_OS_HARDWARE_H +#define _RVL_SDK_OS_HARDWARE_H + +#include "revolution/dvd.h" +#include "revolution/os.h" +#include "revolution/os/OSContext.h" +#include "revolution/os/OSThread.h" +#include "revolution/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// Forward declarations +typedef struct OSExecParams; + +// Derive offsets for use with OSAddress functions +#define __DEF_ADDR_OFFSETS(name, addr) \ + static const u32 OS_PHYS_##name = (addr) - 0x80000000; \ + static const u32 OS_CACHED_##name = (addr); \ + static const u32 OS_UNCACHED_##name = (addr) + (0xC0000000 - 0x80000000); + +// Define a global variable in *CACHED* MEM1. +// Can be accessed directly or with OSAddress functions. +#define OS_DEF_GLOBAL_VAR(type, name, addr) \ + /* Memory-mapped value for direct access */ \ + type OS_##name AT_ADDRESS(addr); \ + __DEF_ADDR_OFFSETS(name, addr) + +// Define a global array in *CACHED* MEM1. +// Can be accessed directly or with OSAddress functions. +#define OS_DEF_GLOBAL_ARR(type, name, arr, addr) \ + /* Memory-mapped value for direct access */ \ + type OS_##name arr AT_ADDRESS(addr); \ + __DEF_ADDR_OFFSETS(name, addr) + +// Define an global variable in the hardware-register range. +#define OS_DEF_HW_REG(type, name, addr) \ + /* Memory-mapped value for direct access */ \ + type OS_##name AT_ADDRESS(addr); + +typedef enum { + OS_BOOT_MAGIC_BOOTROM = 0xD15EA5E, + OS_BOOT_MAGIC_JTAG = 0xE5207C22, +} OSBootMagic; + +typedef struct OSDebugInterface { + /* 0x0 */ int usingDebugger; + /* 0x4 */ u32 exceptionMask; + /* 0x8 */ void* exceptionHook; + /* 0xC */ void* exceptionHookLR; +} OSDebugInterface; + +typedef struct OSBI2 { + /* 0x0 */ u32 dbgMonitorSize; + /* 0x4 */ u32 simulatedMemSize; + /* 0x8 */ u32 argumentOfs; + /* 0xC */ u32 debugFlag; + /* 0x10 */ u32 trackLocation; + /* 0x14 */ u32 trackSize; + /* 0x18 */ u32 countryCode; + u32 WORD_0x1C; + u32 lastInsert; + /* 0x24 */ u32 padSpec; + /* 0x28 */ u32 totalTextDataLimit; + /* 0x2C */ u32 simulatedMem2Size; +} OSBI2; + +/** + * 0x80000000 - 0x80000100 + */ +// clang-format off +OS_DEF_GLOBAL_VAR(OSBootInfo, BOOT_INFO, 0x80000000); +OS_DEF_GLOBAL_VAR(OSDebugInterface, DEBUG_INTERFACE, 0x80000040); +OS_DEF_GLOBAL_ARR(u8, DB_INTEGRATOR_HOOK, [0x24], 0x80000060); +OS_DEF_GLOBAL_VAR(struct OSContext*, CURRENT_CONTEXT_PHYS, 0x800000C0); +OS_DEF_GLOBAL_VAR(u32, PREV_INTR_MASK, 0x800000C4); +OS_DEF_GLOBAL_VAR(u32, CURRENT_INTR_MASK, 0x800000C8); +OS_DEF_GLOBAL_VAR(u32, TV_FORMAT, 0x800000CC); +OS_DEF_GLOBAL_VAR(u32, ARAM_SIZE, 0x800000D0); +OS_DEF_GLOBAL_VAR(struct OSContext*, CURRENT_CONTEXT, 0x800000D4); +OS_DEF_GLOBAL_VAR(struct OSContext*, CURRENT_FPU_CONTEXT, 0x800000D8); +OS_DEF_GLOBAL_VAR(OSThreadQueue, THREAD_QUEUE, 0x800000DC); +OS_DEF_GLOBAL_VAR(OSThread*, CURRENT_THREAD, 0x800000E4); +OS_DEF_GLOBAL_VAR(u32, DEBUG_MONITOR_SIZE, 0x800000E8); +OS_DEF_GLOBAL_VAR(void*, DEBUG_MONITOR, 0x800000EC); +OS_DEF_GLOBAL_VAR(u32, SIMULATED_MEM_SIZE, 0x800000F0); +OS_DEF_GLOBAL_VAR(OSBI2*, DVD_BI2, 0x800000F4); +OS_DEF_GLOBAL_VAR(u32, BUS_CLOCK_SPEED, 0x800000F8); +OS_DEF_GLOBAL_VAR(u32, CPU_CLOCK_SPEED, 0x800000FC); +// clang-format on + +/** + * 0x80003000 - 0x80003F00 + */ +// clang-format off +OS_DEF_GLOBAL_ARR(void*, EXCEPTION_TABLE, [15], 0x80003000); +OS_DEF_GLOBAL_VAR(void*, INTR_HANDLER_TABLE, 0x80003040); +OS_DEF_GLOBAL_VAR(void*, FIRST_REL, 0x800030C8); +OS_DEF_GLOBAL_VAR(void*, LAST_REL, 0x800030CC); +OS_DEF_GLOBAL_VAR(void*, REL_NAME_TABLE, 0x800030D0); +OS_DEF_GLOBAL_VAR(u32, DOL_TOTAL_TEXT_DATA, 0x800030D4); +OS_DEF_GLOBAL_VAR(s64, SYSTEM_TIME, 0x800030D8); +OS_DEF_GLOBAL_VAR(u8, PAD_FLAGS, 0x800030E3); +OS_DEF_GLOBAL_VAR(u16, GC_PAD_3_BTN, 0x800030E4); +OS_DEF_GLOBAL_VAR(volatile u16, DVD_DEVICE_CODE, 0x800030E6); +OS_DEF_GLOBAL_VAR(u8, BI2_DEBUG_FLAG, 0x800030E8); +OS_DEF_GLOBAL_VAR(u8, PAD_SPEC, 0x800030E9); +OS_DEF_GLOBAL_VAR(struct OSExecParams*, DOL_EXEC_PARAMS, 0x800030F0); +OS_DEF_GLOBAL_VAR(u32, PHYSICAL_MEM1_SIZE, 0x80003100); +OS_DEF_GLOBAL_VAR(u32, SIMULATED_MEM1_SIZE, 0x80003104); +OS_DEF_GLOBAL_VAR(void*, USABLE_MEM1_START, 0x8000310C); +OS_DEF_GLOBAL_VAR(void*, USABLE_MEM1_END, 0x80003110); +OS_DEF_GLOBAL_VAR(u32, PHYSICAL_MEM2_SIZE, 0x80003118); +OS_DEF_GLOBAL_VAR(u32, SIMULATED_MEM2_SIZE, 0x8000311C); +OS_DEF_GLOBAL_VAR(void*, ACCESSIBLE_MEM2_END, 0x80003120); +OS_DEF_GLOBAL_VAR(void*, USABLE_MEM2_START, 0x80003124); +OS_DEF_GLOBAL_VAR(void*, USABLE_MEM2_END, 0x80003128); +OS_DEF_GLOBAL_VAR(void*, IPC_BUFFER_START, 0x80003130); +OS_DEF_GLOBAL_VAR(void*, IPC_BUFFER_END, 0x80003134); +OS_DEF_GLOBAL_VAR(u32, HOLLYWOOD_REV, 0x80003138); +OS_DEF_GLOBAL_VAR(u32, IOS_VERSION, 0x80003140); +OS_DEF_GLOBAL_VAR(u32, IOS_BUILD_DATE, 0x80003144); +OS_DEF_GLOBAL_VAR(void*, IOS_HEAP_START, 0x80003148); +OS_DEF_GLOBAL_VAR(void*, IOS_HEAP_END, 0x8000314C); +OS_DEF_GLOBAL_VAR(u32, GDDR_VENDOR_CODE, 0x80003158); +OS_DEF_GLOBAL_VAR(u8, BOOT_PROGRAM_TARGET, 0x8000315C); +OS_DEF_GLOBAL_VAR(u8, APPLOADER_TARGET, 0x8000315D); +OS_DEF_GLOBAL_VAR(int, MIOS_SHUTDOWN_FLAG, 0x80003164); +OS_DEF_GLOBAL_VAR(u32, CURRENT_APP_NAME, 0x80003180); +OS_DEF_GLOBAL_VAR(u8, CURRENT_APP_TYPE, 0x80003184); +OS_DEF_GLOBAL_VAR(u32, MINIMUM_IOS_VERSION, 0x80003188); +OS_DEF_GLOBAL_VAR(u32, NAND_TITLE_LAUNCH_CODE, 0x8000318C); +OS_DEF_GLOBAL_VAR(u32, NAND_TITLE_RETURN_CODE, 0x80003190); +OS_DEF_GLOBAL_VAR(u32, BOOT_PARTITION_TYPE, 0x80003194); +OS_DEF_GLOBAL_VAR(u32, BOOT_PARTITION_OFFSET, 0x80003198); +OS_DEF_GLOBAL_ARR(u8, NWC24_USER_ID_BUFFER, [32], 0x800031C0); +OS_DEF_GLOBAL_VAR(u64, NWC24_USER_ID, 0x800031C0); +OS_DEF_GLOBAL_ARR(u8, SC_PRDINFO, [0x100], 0x80003800); +// clang-format on + +/** + * PI hardware globals + */ +volatile u32 PI_HW_REGS[] AT_ADDRESS(0xCC003000); +typedef enum { + PI_INTSR, //!< 0xCC003000 + PI_INTMR, //!< 0xCC003004 + PI_REG_0x8, //!< 0xCC003008 + PI_REG_0xC, //!< 0xCC00300C + PI_REG_0x10, //!< 0xCC003010 + PI_REG_0x14, //!< 0xCC003014 + PI_REG_0x18, //!< 0xCC003018 + PI_REG_0x1C, //!< 0xCC00301C + PI_REG_0x20, //!< 0xCC003020 + PI_RESET, //!< 0xCC003024 + // . . . +} PIHwReg; + +// INTSR - Interrupt Cause Register +#define PI_INTSR_ERROR (1 << 0) +#define PI_INTSR_RSW (1 << 1) +#define PI_INTSR_DI (1 << 2) +#define PI_INTSR_SI (1 << 3) +#define PI_INTSR_EXI (1 << 4) +#define PI_INTSR_AI (1 << 5) +#define PI_INTSR_DSP (1 << 6) +#define PI_INTSR_MEM (1 << 7) +#define PI_INTSR_VI (1 << 8) +#define PI_INTSR_PE_TOKEN (1 << 9) +#define PI_INTSR_PE_FINISH (1 << 10) +#define PI_INTSR_CP (1 << 11) +#define PI_INTSR_DEBUG (1 << 12) +#define PI_INTSR_HSP (1 << 13) +#define PI_INTSR_ACR (1 << 14) +#define PI_INTSR_RSWST (1 << 16) + +// INTMR - Interrupt Mask Register +#define PI_INTMR_ERROR (1 << 0) +#define PI_INTMR_RSW (1 << 1) +#define PI_INTMR_DI (1 << 2) +#define PI_INTMR_SI (1 << 3) +#define PI_INTMR_EXI (1 << 4) +#define PI_INTMR_AI (1 << 5) +#define PI_INTMR_DSP (1 << 6) +#define PI_INTMR_MEM (1 << 7) +#define PI_INTMR_VI (1 << 8) +#define PI_INTMR_PE_TOKEN (1 << 9) +#define PI_INTMR_PE_FINISH (1 << 10) +#define PI_INTMR_CP (1 << 11) +#define PI_INTMR_DEBUG (1 << 12) +#define PI_INTMR_HSP (1 << 13) +#define PI_INTMR_ACR (1 << 14) + +/** + * MI hardware registers + */ +volatile u16 MI_HW_REGS[] AT_ADDRESS(0xCC004000); +typedef enum { + MI_PAGE_MEM0_H, //!< 0xCC004000 + MI_PAGE_MEM0_L, //!< 0xCC004002 + MI_PAGE_MEM1_H, //!< 0xCC004004 + MI_PAGE_MEM1_L, //!< 0xCC004006 + MI_PAGE_MEM2_H, //!< 0xCC004008 + MI_PAGE_MEM2_L, //!< 0xCC00400A + MI_PAGE_MEM3_H, //!< 0xCC00400C + MI_PAGE_MEM3_L, //!< 0xCC00400E + MI_PROT_MEM0, //!< 0xCC004010 + MI_PROT_MEM1, //!< 0xCC004012 + MI_PROT_MEM2, //!< 0xCC004014 + MI_PROT_MEM3, //!< 0xCC004016 + MI_REG_0x18, //!< 0xCC004018 + MI_REG_0x1A, //!< 0xCC00401A + MI_INTMR, //!< 0xCC00401C + MI_INTSR, //!< 0xCC00401E + MI_REG_0x20, //!< 0xCC004020 + MI_ADDRLO, //!< 0xCC004022 + MI_ADDRHI, //!< 0xCC004024 + MI_REG_0x26, //!< 0xCC004026 + MI_REG_0x28, //!< 0xCC004028 + // . . . +} MIHwReg; + +// INTMR - Interrupt Mask Register +#define MI_INTMR_MEM0 (1 << 0) +#define MI_INTMR_MEM1 (1 << 1) +#define MI_INTMR_MEM2 (1 << 2) +#define MI_INTMR_MEM3 (1 << 3) +#define MI_INTMR_ADDR (1 << 4) + +// INTSR - Interrupt Cause Register +#define MI_INTSR_MEM0 (1 << 0) +#define MI_INTSR_MEM1 (1 << 1) +#define MI_INTSR_MEM2 (1 << 2) +#define MI_INTSR_MEM3 (1 << 3) +#define MI_INTSR_ADDR (1 << 4) + +/** + * DI hardware globals + */ +// clang-format off +OS_DEF_HW_REG(volatile unsigned long, DI_DMA_ADDR, 0xCD006014); +OS_DEF_HW_REG(volatile unsigned long, DI_CONFIG, 0xCD006024); +// clang-format on + +/** + * Misc/unknown globals + */ +// clang-format off +OS_DEF_HW_REG(volatile unsigned long, UNK_CD000034, 0xCD000034); +OS_DEF_HW_REG(volatile unsigned long, UNK_CD800180, 0xCD800180); +OS_DEF_HW_REG(volatile unsigned long, UNK_CD8001CC, 0xCD8001CC); +OS_DEF_HW_REG(volatile unsigned long, UNK_CD8001D0, 0xCD8001D0); +// clang-format on + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/revolution/os/__start.c b/src/revolution/os/__start.c new file mode 100644 index 00000000000..245e8e0027e --- /dev/null +++ b/src/revolution/os/__start.c @@ -0,0 +1,376 @@ +#include "revolution/revolution.h" +#include "revolution/os/OSHardware.h" +#include "__ppc_eabi_linker.h" + +extern void InitMetroTRK(); +extern void exit(int); +extern int main(int argc, char* argv[]); +extern void __init_user(void); +extern void InitMetroTRK_BBA(void); +extern void DBInit(); + +__declspec(section ".init") extern void __check_pad3(void); +__declspec(section ".init") extern void __set_debug_bba(void); +__declspec(section ".init") extern u8 __get_debug_bba(void); +__declspec(section ".init") extern void __start(void); +__declspec(section ".init") extern void __init_registers(void); +__declspec(section ".init") extern void __init_data(void); +__declspec(section ".init") extern void __init_hardware(void); +__declspec(section ".init") extern void __flush_cache(void* addr, u32 size); + +extern u8 Debug_BBA; + +__declspec(section ".init") void __check_pad3(void) { + if ((*(u16*)0x800030E4 & 0xEEF) == 0xEEF) { + OSResetSystem(0, 0, 0); + } +} + +__declspec(section ".init") void __set_debug_bba(void) { Debug_BBA = 1; } + +__declspec(section ".init") u8 __get_debug_bba(void) { return Debug_BBA; } + +__declspec(section ".init") __declspec(weak) asm void __start(void) { +#ifdef __MWERKS__ // clang-format off + nofralloc + + // Setup hardware + bl __init_registers + bl __init_hardware + + // Create first stack frame + li r0, -1 + // Parameter save + stwu r1, -8(r1) + // LR save (-1 to signify first frame) + stw r0, 4(r1) + // Back chain (-1 to signify first frame) + stw r0, 0(r1) + + // Setup ROM/BSS + bl __init_data + + // Clear debugger exception mask + li r0, 0 + lis r6, (OS_DEBUG_INTERFACE + OSDebugInterface.exceptionMask)@ha + addi r6, r6, (OS_DEBUG_INTERFACE + OSDebugInterface.exceptionMask)@l + stw r0, 0(r6) + + /** + * Check the BI2 debug flag to determine how to call InitMetroTRK. + * + * This can be done by either checking the value from the DVD + * (OSBI2.debugFlag), or by checking the global OS_BI2_DEBUG_FLAG. + * + * If the DVD BI2 is available, it is prioritized over the global value. + */ + lis r6, OS_DVD_BI2@ha + addi r6, r6, OS_DVD_BI2@l + lwz r6, 0(r6) + cmplwi r6, 0 + beq _no_dvd_bi2 // <- NULL BI2, try the OS global + + // Use the DVD's flag + lwz r7, OSBI2.debugFlag(r6) + b _handle_bi2_debug_flag + + /** + * At this point, we do one last check to decide whether we want to + * setup the TRK debugger. + * + * If the OS boot info specifies an arena hi, we grab the BI2 debug + * flag using the global OS_BI2_DEBUG_FLAG. + * + * (This must be some heuristic, but I don't understand it) + */ +_no_dvd_bi2: + lis r5, (OS_BOOT_INFO + OSBootInfo.arenaHi)@ha + addi r5, r5, (OS_BOOT_INFO + OSBootInfo.arenaHi)@l + lwz r5, 0(r5) + cmplwi r5, 0 + beq _check_for_exec_args // <- NULL arena hi, don't setup the debugger + + // Grab the global BI2 debug flag + lis r7, OS_BI2_DEBUG_FLAG@ha + addi r7, r7, OS_BI2_DEBUG_FLAG@l + lwz r7, 0(r7) + + /** + * The BI2 debug flag/level decides how to set up TRK. + * + * Most importantly, it decides how InitMetroTRKCommTable is called, by + * specifying a type of hardware. + * + * ID 1: NDEV hardware + * ID 2: BBA hardware + * Anything else: "Unknown" to TRK, defaults to GDEV hardware + * + * BI2 debug level maps to TRK comm hardware ID as follows: + * - BI2 Level 2 -> ID 0 (GDEV) + * - BI2 Level 3 -> ID 1 (NDEV) + * - BI2 Level 4 -> ID 2 (BBA) + */ +_handle_bi2_debug_flag: + // BI2 Debug Level 3: Init TRK as GDEV hardware + li r5, 0 + cmplwi r7, 2 + beq _call_init_metro_trk + + // BI2 Debug Level 2: Init TRK as NDEV hardware + cmplwi r7, 3 + li r5, 1 + beq _call_init_metro_trk + + // BI2 Debug Level 4: Init TRK as BBA hardware + // *Any other debug level is ignored* + cmplwi r7, 4 + bne _check_for_exec_args // <- Ignore debug level, goto next step + /** + * I think at one point this used to call InitMetroTRK as they set + * r5 here; however, in this version it goes unused. + * + * What currently happens is __set_debug_bba sets Debug_BBA, which + * will result in a call to InitMetroTRK_BBA after the OS is + * initialized. + * + * Maybe this is a leftover from how things worked before + * InitMetroTRK_BBA was written? + */ + li r5, 2 + bl __set_debug_bba + b _check_for_exec_args + + /** + * Call InitMetroTRK + * The MetroTRK hardware ID is specified in r5 + */ +_call_init_metro_trk: + lis r6, InitMetroTRK@ha + addi r6, r6, InitMetroTRK@l + mtlr r6 + blrl + + /** + * After setting up the hardware and the debugger, we next setup + * the program arguments. This label checks whether any arguments + * exist. + * + * BI2 contains an offset from itself to the argument data, which + * is formatted as follows: + * + * typedef struct BI2Args { + * int argc; + * union { + * char* argument; + * u32 offset; + * } argv[]; + * } BI2Args; + */ +_check_for_exec_args: + lis r6, OS_DVD_BI2@ha + addi r6, r6, OS_DVD_BI2@l + lwz r5, 0(r6) + cmplwi r5, 0 + beq+ _no_args // <- No BI2 to get args from + + lwz r6, OSBI2.argumentOfs(r5) + cmplwi r6, 0 + beq+ _no_args // <- Invalid argument offset in BI2 + + // Calculate pointer to argument data + add r6, r5, r6 + // First integer value is the argument count + lwz r14, 0(r6) + cmplwi r14, 0 + beq _no_args // <- Argument count is zero + + // BI2 args + 0x8, used to adjust MEM1 later + addi r15, r6, 4 + // Move argc to the counter to prepare the loop + mtctr r14 + + /** + * This loop unpacks the arguments by converting them from offsets + * to pointers, in-place. + * + * The offsets are relative to the start of the BI2, so we just + * add the offset to the BI2 pointer and write it back. + */ +_unpack_args_loop: + // Skip over argc + addi r6, r6, 4 + // Load argument offset + lwz r7, 0(r6) + // Convert offset to pointer + add r7, r7, r5 + // Store pointer + stw r7, 0(r6) + bdnz _unpack_args_loop + + /** + * Both the MEM1 arena hi and the MEM1 limit are adjusted to + * preserve the BI2 args. + * + * They are set to eight bytes into the BI2 arguments, + * rounded down to the nearest 32 bytes. + */ + lis r5, (OS_BOOT_INFO + OSBootInfo.arenaHi)@ha + addi r5, r5, (OS_BOOT_INFO + OSBootInfo.arenaHi)@l + clrrwi r7, r15, 5 + stw r7, 0(r5) + + lis r5, OS_USABLE_MEM1_END@ha + addi r5, r5, OS_USABLE_MEM1_END@l + clrrwi r7, r15, 5 + stw r7, 0(r5) + b _init_os + + /** + * Either there was no BI2 available, or it did not contain any arguments + */ +_no_args: + li r14, 0 // argc = 0 + li r15, 0 // argv = NULL + + /** + * Here, the OS and its debug monitor are initialized, and + * then we check if we should call __check_pad3. + * + * __check_pad3 is called before future initialization if the disk + * drive device code is 0x0001, or if the OS' inquiry fails (emulation + * or some debug hardware?) + * + * The apploader reads the button state of the fourth GCN controller + * and writes it to GC_PAD_3_BTN (zero-indexed), which is used in + * __check_pad3. WiiBrew states that this is for GameCube NR disc + * support, so that could explain the unusual DVD device code address. + */ +_init_os: + // Initialize the OS and its debug monitor + bl DBInit + bl OSInit + + // Load DVD device code address + lis r4, OS_DVD_DEVICE_CODE@ha + addi r4, r4, OS_DVD_DEVICE_CODE@l + lhz r3, 0(r4) + // Check whether OS inquiry failed + andi. r5, r3, DVD_DEVICE_CODE_READ + beq _call_check_pad3 // <- Bit 0 is NOT set (fail) + // 0x0001 may be a real ID or a failsafe (see OS.c:InquiryCallback) + andi. r3, r3, (~DVD_DEVICE_CODE_READ) & 0xFFFF + cmplwi r3, 0x0001 + bne _check_debug_bba // <- NOT 0x0001 device code address +_call_check_pad3: + bl __check_pad3 + + /** + * If the BI2 debug level from earlier was set to four, we need to + * initialize the debugger for BBA hardware. + */ +_check_debug_bba: + bl __get_debug_bba + cmplwi r3, 1 + bne _after_init_metro_trk_bba // <- Debug_BBA == false + bl InitMetroTRK_BBA + + /** + * 1. Initialize C++ runtime + * 2. Call main(argc, argv) + * 3. Teardown C++ runtime + */ +_after_init_metro_trk_bba: + bl __init_user + mr r3, r14 + mr r4, r15 + bl main + b exit // <- Will halt CPU +#endif // clang-format on +} + +static inline void __copy_rom_section(void* dst, const void* src, size_t size) { + if (size == 0 || dst == src) { + return; + } + + memcpy(dst, src, size); + __flush_cache(dst, size); +} + +static inline void __init_bss_section(void* dst, size_t size) { + if (size == 0) { + return; + } + + memset(dst, 0, size); +} + +__declspec(section ".init") static asm void __init_registers(void) { +#ifdef __MWERKS__ // clang-format off + nofralloc + + li r0, 0 + li r3, 0 + li r4, 0 + li r5, 0 + li r6, 0 + li r7, 0 + li r8, 0 + li r9, 0 + li r10, 0 + li r11, 0 + li r12, 0 + li r14, 0 + li r15, 0 + li r16, 0 + li r17, 0 + li r18, 0 + li r19, 0 + li r20, 0 + li r21, 0 + li r22, 0 + li r23, 0 + li r24, 0 + li r25, 0 + li r26, 0 + li r27, 0 + li r28, 0 + li r29, 0 + li r30, 0 + li r31, 0 + + lis r1, _stack_addr@h + ori r1, r1, _stack_addr@l + lis r2, _SDA2_BASE_@h + ori r2, r2, _SDA2_BASE_@l + lis r13, _SDA_BASE_@h + ori r13, r13, _SDA_BASE_@l + + blr +#endif // clang-format on +} + +__declspec(section ".init") static void __init_data(void) { + const __rom_copy_info* rs; + const __bss_init_info* bs; + + rs = _rom_copy_info; + while (1) { + if (rs->size == 0) { + break; + } + + __copy_rom_section(rs->addr, rs->rom, rs->size); + rs++; + } + + bs = _bss_init_info; + while (1) { + if (bs->size == 0) { + break; + } + + __init_bss_section(bs->addr, bs->size); + bs++; + } +}