mirror of
https://github.com/emu-russia/pureikyubu.git
synced 2025-04-02 10:42:15 -04:00
Docs: New TODO moved to EMU, old renamed to old_todo Docs: Old coding style is deprecated (old_style.txt) Docs: Removed mention about CubeDocumented and fixed old emails RE: boot.s, proved first asm line (lis instruction parameter) RE: Added PAL and NTSC Boot and IPL IDA files RE: Some work on NTSC IPL (identified many lib calls, including OS, GX) RE: Added EXI Bootrom descrambler by segher RE: GXInit RE: Internal GX lib structures (GXPrivate.h) RE: More details on lomem (OS versions) RE: OSInit and OS.c RE: OSAlloc (heap allocator) RE: Very first code of Metrowerk runtime (__start.c) Docs: Added copy of http://gcdev.narod.ru Source\Utils: Command processor (Cmd.c) Source\Utils: File wrapper Source\Utils: Gekko disasm cleaned up and ported to plain C Source\Utils: Double-linked lists Source\Utils: Ported old Profiler code Source\Utils: String utils
704 lines
23 KiB
Text
704 lines
23 KiB
Text
|
|
Gamecube apploader reverse engineering.
|
|
|
|
This reversing is not pretending to be compilable, and can be used for
|
|
education purposes only. I dont hold any responsibility for damage of your
|
|
Gamecube clock settings or memory card saves or else, produced from using
|
|
code/information represented in this document.
|
|
|
|
Version 1.1. (26 Apr 2005)
|
|
Additions and corrections by tmbinc.
|
|
|
|
------------------------------------------------------------------------------
|
|
|
|
Overview.
|
|
|
|
Complete Luigi's Apploader. Reverse work by org <ogamespec@gmail.com>
|
|
Other games seems to contain the same code with minor changes due to
|
|
revision, so you may think, that apploader is the same for all games.
|
|
Except Datel's FreeLoader and ActionReplay DVDs, which are using custom
|
|
apploader binary (simplified Nintendo's apploader).
|
|
|
|
Apploader is a small program layer between GC bootrom and main
|
|
application. Since main application's executable binary format may vary,
|
|
apploader is needed to boot executable in main memory, using bootrom's
|
|
low-level DVD read routines. In practice, all known game apploaders are using
|
|
Nintendo's DOL format for executable files.
|
|
|
|
Format of apploader binary is simple. First goes apploader header, and then
|
|
actual apploader code.
|
|
|
|
Apploader header :
|
|
|
|
typedef struct
|
|
{
|
|
char revision[16]; // Revision compile date (example "2001/12/17")
|
|
u32 entryPoint; // Apploader's entrypoint
|
|
s32 size; // Size of apploader code
|
|
s32 trailerSize; // Size of safe trailer
|
|
u8 padding[4]; // zeroes
|
|
} LDRImage;
|
|
|
|
Full apploader size is sum of size and trailerSize, rounded up to 32 bytes.
|
|
Trailer is need for future apploader versions.
|
|
|
|
Apploader is located at 0x2440 offset on DVD and loaded by bootrom at
|
|
0x81200000 address. Then bootrom jumps to apploader's entrypoint :
|
|
|
|
void AplEntry(
|
|
void (**Init)(void (*OSReportCallback)(char *msg, ...)),
|
|
BOOL (**Main)(void **addr, s32 *length, s32 *offset),
|
|
void* (**Close)())
|
|
{
|
|
// Return location of apploader's Init, Main and Close routines.
|
|
*Init = AplInit;
|
|
*Main = AplMain;
|
|
*Close = AplClose;
|
|
}
|
|
|
|
After executing of entrypoint, bootrom will call in order: apploader's Init,
|
|
then Main, then Close. Main is called in loop, until it will not return 0.
|
|
|
|
--------------------------------------------------------------------------
|
|
|
|
PART I. Apploader Init.
|
|
|
|
Init call is clearing apploader's internal data, and saving there callback
|
|
of OSReport call (which is located inside IPL). OSReport is used to output
|
|
some debug messages, during apploader runtime.
|
|
|
|
Apploader's internal data :
|
|
|
|
// Some DVD transfers require multiple times to load.
|
|
BOOL SecondTimeForThePart;
|
|
void* SecondTimeAddr;
|
|
s32 SecondTimeLength;
|
|
s32 SecondTimeOffset;
|
|
int SecondTimePart;
|
|
|
|
DVDBB2 bb2; // DVD boot block
|
|
DolImage DolImage; // main DOL executable header
|
|
|
|
int Section; // DOL section
|
|
int Part; // Apploader's step
|
|
u32 +0x148 // ?? only cleared in Init
|
|
|
|
// OSReport routine itself is placed somewhere in IPL
|
|
void (*OSReport)(char *msg, ...);
|
|
|
|
#pragma pack(32)
|
|
// First 32 bytes of DVDBI2 will be loaded here. This is a bit hacky.
|
|
// We dont use whole DVDBI2 (its too big), to save some memory.
|
|
// 32 bytes - because its minimum DVD DMA transfer size (?)
|
|
s32 debugMonSize;
|
|
s32 simMemSize;
|
|
u32 argOffset;
|
|
u32 debugFlag;
|
|
int FSTHigh; // 1, if FST is resided on DVD above DOL
|
|
u8 dummy[12]; // ending bytes
|
|
#pragma pack(0)
|
|
|
|
|
|
void AplInit(void (*OSReportCallback)(char *msg, ...))
|
|
{
|
|
// Clear some structures
|
|
memset(&bb2, 0, sizeof(DVDBB2));
|
|
memset(&DolImage, 0, sizeof(DolImage));
|
|
|
|
Section = 0;
|
|
Part = 0;
|
|
[+0x148] = 0;
|
|
|
|
OSReport = OSReportCallback; // Save report callback
|
|
|
|
// Print apploader version info
|
|
OSReport("Apploader Initialized. $ Revision: 28 $\n");
|
|
OSReport("This Apploader built %s %s\n", __DATE__, __TIME__);
|
|
}
|
|
|
|
--------------------------------------------------------------------------
|
|
|
|
PART II. Apploader Main.
|
|
|
|
Main is called in loop by IPL, until all data not loaded. IPL is using Main
|
|
output parameters, to load data from DVD. IPL keeps loading new data, until
|
|
Main will not return 0, signaling that all data loaded.
|
|
|
|
Just read through comments to understand code.
|
|
|
|
BOOL AplMain(void **addr, s32 *length, s32 *offset)
|
|
{
|
|
OSLoMem *LoMem = OSPhysicalToCached(0x0000);
|
|
|
|
switch(Part)
|
|
{
|
|
// Load BB2 structure.
|
|
case 0:
|
|
case 1:
|
|
*addr = &bb2;
|
|
*length = sizeof(DVDBB2);
|
|
*offset = DVD_BB2_OFFSET;
|
|
|
|
Part = 2;
|
|
DCInvalidateRange(*addr, *length);
|
|
break;
|
|
|
|
// Check FST length and load first 32 bytes of BI2 structure.
|
|
case 2:
|
|
u32 FSTLength = bb2.FSTLength;
|
|
u32 FSTMaxLength = bb2.FSTMaxLength;
|
|
|
|
if(FSTLength > FSTMaxLength)
|
|
{
|
|
OSReport(
|
|
"APPLOADER ERROR >>> FSTLength(%d) in BB2 is greater "
|
|
"than FSTMaxLength(%d)\n", FSTLength, FSTMaxLength );
|
|
PPCHalt();
|
|
}
|
|
|
|
*addr = &debugMonSize;
|
|
*length = 32;
|
|
*offset = DVD_BI2_OFFSET;
|
|
|
|
Part = 3;
|
|
DCInvalidateRange(*addr, *length);
|
|
break;
|
|
|
|
// Setup some OS lomem variables by BI2 values, and check memory configuration.
|
|
// Copy DVD BI2 in main memory.
|
|
case 3:
|
|
__OSDebugMonSize = debugMonSize;
|
|
__OSDebugMon = OSRoundDown32B(__OSPhysMemSize - __OSDebugMonSize);
|
|
__OSSimMemSize = simMemSize;
|
|
|
|
// Check debug monitor size alignment
|
|
if(__OSDebugMonSize & 31)
|
|
{
|
|
OSReport(
|
|
"APPLOADER ERROR >>> Debug monitor size (%d) should "
|
|
"be a multiple of 32\n", __OSDebugMonSize );
|
|
PPCHalt();
|
|
}
|
|
|
|
// Check console simulated memory size alignment
|
|
if(__OSSimMemSize & 31)
|
|
{
|
|
OSReport(
|
|
"APPLOADER ERROR >>> simulated memory size (%d) "
|
|
"should be a multiple of 32\n", __OSSimMemSize );
|
|
PPCHalt();
|
|
}
|
|
|
|
if(__OSSimMemSize == 0)
|
|
{
|
|
__OSSimMemSize = __OSPhysMemSize;
|
|
}
|
|
|
|
if(__OSSimMemSize < __OSPhysMemSize)
|
|
{
|
|
if(__OSDebugMonSize >= (__OSPhysMemSize - __OSSimMemSize))
|
|
{
|
|
OSReport(
|
|
"APPLOADER ERROR >>> [Physical memory size(0x%x)] "
|
|
"- [Console simulated memory size(0x%x)]\n"
|
|
"APPLOADER ERROR >>> must be greater than debug
|
|
"monitor size(0x%x)\n",
|
|
__OSPhysMemSize, __OSSimMemSize, __OSDebugMonSize );
|
|
PPCHalt();
|
|
}
|
|
|
|
// Move down FST address
|
|
bb2.FSTAddress = OSRoundDown32B(__OSSimMemSize - bb2.FSTMaxSize);
|
|
}
|
|
else
|
|
{
|
|
if(__OSSimMemSize == __OSPhysMemSize)
|
|
{
|
|
bb2.FSTAddress = OSRoundDown32B(__OSDebugMon - bb2.FSTMaxSize);
|
|
}
|
|
else
|
|
{
|
|
OSReport(
|
|
"APPLOADER ERROR >>> Physical memory size is 0x%x bytes.\n"
|
|
"APPLOADER ERROR >>> Console simulated memory size "
|
|
"must be smaller than or equal to the Physical memory size\n",
|
|
__OSPhysMemSize, __OSSimMemSize );
|
|
PPCHalt();
|
|
}
|
|
}
|
|
|
|
// Load whole BI2 in main memory. Reside it before FST.
|
|
__OSBootInfo2 = bb2.FSTAddress - OS_BI2_SIZE;
|
|
|
|
*addr = __OSBootInfo2;
|
|
*length = OS_BI2_SIZE;
|
|
*offset = DVD_BI2_OFFSET;
|
|
|
|
Part = 4;
|
|
DCInvalidateRange(*addr, *length);
|
|
break;
|
|
|
|
// Load FST, only if FST placed before boot file.
|
|
case 4:
|
|
FSTHigh = bb2.bootFilePosition < bb2.FSTPosition;
|
|
if(FSTHigh)
|
|
{
|
|
// Continue on part 5. Load FST later.
|
|
Part = 5;
|
|
}
|
|
else
|
|
{
|
|
*addr = bb2.FSTAddress;
|
|
*length = OSRoundUp32B(bb2.FSTLength);
|
|
*offset = bb2.FSTPosition;
|
|
|
|
Part = 5;
|
|
|
|
if(*addr > 0x81700000)
|
|
{
|
|
OSReport(
|
|
"APPLOADER ERROR >>> Illegal FST "
|
|
"destination address! (0x%x)\n", *addr );
|
|
PPCHalt();
|
|
}
|
|
|
|
DCInvalidateRange(*addr, *length);
|
|
LoadBigData(addr, length, offset, &Part);
|
|
break;
|
|
}
|
|
|
|
// Load main DOL header.
|
|
case 5:
|
|
*addr = &DolImage;
|
|
*length = sizeof(DolImage);
|
|
*offset = bb2.bootFilePosition;
|
|
|
|
Part = 6;
|
|
DCInvalidateRange(*addr, *length);
|
|
break;
|
|
|
|
// Check out DOL properties. Clear BSS.
|
|
case 6:
|
|
s32 dolSize = DOLSize();
|
|
|
|
// Halt apploader, if DOL size exceeds limit.
|
|
if( (dolSize > __OSBootInfo2->dolLimit) && __OSBootInfo2->dolLimit)
|
|
{
|
|
OSReport(
|
|
"APPLOADER ERROR >>> Total size of text/data sections "
|
|
"of the dol file are too big (%d(0x%08x) bytes). "
|
|
"Currently the limit is set as %d(0x%08x) bytes\n",
|
|
dolSize, __OSBootInfo2->dolLimit );
|
|
PPCHalt();
|
|
}
|
|
|
|
// Save DOL size in OS globals
|
|
if(FSTHigh) __OSDOLSize = dolSize + OSRoundUp32B(bb2.FSTLength);
|
|
else __OSDOLSize = dolSize;
|
|
|
|
// Address limit?
|
|
#define DOL_ADDRESS_LIMIT 0x80700000 // production boards
|
|
#define DOL_ADDRESS_LIMITD 0x81200000 // development boards
|
|
if(!(OSGetConsoleType() & OS_CONSOLE_DEVELOPMENT))
|
|
{
|
|
if(AddressLimit(DOL_ADDRESS_LIMIT) == FALSE)
|
|
{
|
|
OSReport(
|
|
"APPLOADER ERROR >>> One of the sections in the dol file "
|
|
"exceeded its boundary. All the sections should not exceed "
|
|
"0x%08x (production mode).\n", DOL_ADDRESS_LIMIT );
|
|
PPCHalt();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Check twice : first for production mode, then for development.
|
|
|
|
if(AddressLimit(DOL_ADDRESS_LIMIT) == FALSE)
|
|
{
|
|
OSReport(
|
|
"APPLOADER WARNING >>> One of the sections in the dol file "
|
|
"exceeded its boundary. All the sections should not exceed "
|
|
"0x%08x in production mode.\n", DOL_ADDRESS_LIMIT );
|
|
// Do not halt apploader (show only warning).
|
|
}
|
|
|
|
if(AddressLimit(DOL_ADDRESS_LIMITD) == FALSE)
|
|
{
|
|
OSReport(
|
|
"APPLOADER ERROR >>> One of the sections in the dol file "
|
|
"exceeded its boundary. All the sections should not exceed "
|
|
"0x%08x (development mode).\n", DOL_ADDRESS_LIMITD );
|
|
PPCHalt();
|
|
}
|
|
}
|
|
|
|
// Clear BSS
|
|
memset(DolImage.bss, 0, DolImage.bssLen);
|
|
DCFlushRange(DolImage.bss, DolImage.bssLen);
|
|
|
|
Part = 7;
|
|
|
|
// Load text sections.
|
|
case 7:
|
|
if(Section<DOL_MAX_TEXT)
|
|
{
|
|
if(DolImage.textData[Section])
|
|
{
|
|
*addr = DolImage.text[Section];
|
|
*length = OSRoundUp32B(DolImage.textLen[Section]);
|
|
*offset = bb2.bootFilePosition + DolImage.textData[Section];
|
|
|
|
if( (*addr + *length) > 0x81200000 )
|
|
{
|
|
OSReport(
|
|
"APPLOADER ERROR >>> Too big text segment! (0x%x - 0x%x)\n",
|
|
*addr, *addr + *length );
|
|
PPCHalt();
|
|
}
|
|
|
|
Section++;
|
|
DCInvalidateRange(*addr, *length);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Section = 0;
|
|
Part = 8;
|
|
}
|
|
|
|
// Load data sections.
|
|
case 8:
|
|
if(Section<DOL_MAX_DATA)
|
|
{
|
|
if(DolImage.dataData[Section])
|
|
{
|
|
*addr = DolImage.data[Section];
|
|
*length = OSRoundUp32B(DolImage.dataLen[Section]);
|
|
*offset = bb2.bootFilePosition + DolImage.dataData[Section];
|
|
|
|
if( (*addr + *length) > 0x81200000 )
|
|
{
|
|
OSReport(
|
|
"APPLOADER ERROR >>> Too big data segment! (0x%x - 0x%x)\n",
|
|
*addr, *addr + *length );
|
|
PPCHalt();
|
|
}
|
|
|
|
Section++;
|
|
DCInvalidateRange(*addr, *length);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Section = 0;
|
|
Part = 9;
|
|
}
|
|
|
|
// Load FST, only if FST placed after boot file.
|
|
case 9:
|
|
if(FSTHigh)
|
|
{
|
|
*addr = bb2.FSTAddress;
|
|
*length = OSRoundUp32B(bb2.FSTLength);
|
|
*offset = bb2.FSTPosition;
|
|
|
|
Part = 10;
|
|
|
|
if(*addr > 0x81700000)
|
|
{
|
|
OSReport(
|
|
"APPLOADER ERROR >>> Illegal FST "
|
|
"destination address! (0x%x)\n", *addr );
|
|
PPCHalt();
|
|
}
|
|
|
|
DCInvalidateRange(*addr, *length);
|
|
LoadBigData(addr, length, offset, &Part);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// Continue on part 10.
|
|
Part = 10;
|
|
}
|
|
|
|
// Init misc. low memory variables. Continue on part 11.
|
|
case 10:
|
|
InitLoMem(LoMem);
|
|
Part = 11;
|
|
|
|
// Read button state of 4th controller and save it in OS global.
|
|
// It's for testing pre-master dvds (NR Discs). But as NR discs gets
|
|
// written without change to the dvdroms the retail apploaders contains
|
|
// it, too. If you plug in a special device (dunno what it is exactly -
|
|
// controller with special id? some bit set?) the apploader starts making
|
|
// a crc of the whole disc and compare that to a crc stored on memory card.
|
|
case 11:
|
|
PADStatus padst;
|
|
PadRead(PAD_CHAN3, &padst);
|
|
__OSPADButton = padst.button;
|
|
|
|
// All data loaded. End of apploader.
|
|
return FALSE;
|
|
|
|
// This part will be called, if DVD need to load more, than 64 sectors.
|
|
case 12:
|
|
ASSERT(SecondTimeForThePart == TRUE);
|
|
LoadBigData(addr, length, offset, &Part);
|
|
break;
|
|
|
|
// Unknown part. Abort data loading.
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
// We need to load more data. Tell IPL to do that.
|
|
return TRUE;
|
|
}
|
|
|
|
--------------------------------------------------------------------------
|
|
|
|
PART III. Apploader Close.
|
|
|
|
Close call is executed after apploader's Main.
|
|
|
|
void* AplClose(void)
|
|
{
|
|
// Return entrypoint of main DOL executable to IPL
|
|
return DolImage.entry;
|
|
}
|
|
|
|
Now IPL jumps to DOL's entrypoint (__start).
|
|
|
|
------------------------------------------------------------------------------
|
|
|
|
Appendix A: Helper calls.
|
|
|
|
Calculate full size of DOL text and data sections.
|
|
|
|
s32 DOLSize(void)
|
|
{
|
|
DolImage *dol = &DolImage;
|
|
s32 totalBytes = 0, i;
|
|
|
|
for(i=0; i<DOL_MAX_TEXT; i++)
|
|
{
|
|
if(dol->textData[i])
|
|
{
|
|
// Aligned to 32 byte boundary
|
|
totalBytes += OSRoundUp32B(dol->textLen[i]);
|
|
}
|
|
}
|
|
|
|
for(i=0; i<DOL_MAX_DATA; i++)
|
|
{
|
|
if(dol->dataData[i])
|
|
{
|
|
// Aligned to 32 byte boundary
|
|
totalBytes += OSRoundUp32B(dol->dataLen[i]);
|
|
}
|
|
}
|
|
|
|
return totalBytes;
|
|
}
|
|
|
|
Return FALSE, if any DOL section, including BSS exceed address limit.
|
|
|
|
BOOL AddressLimit(u32 limit)
|
|
{
|
|
DolImage *dol = &DolImage;
|
|
s32 i, end;
|
|
|
|
// Text sections
|
|
for(i=0; i<DOL_MAX_TEXT; i++)
|
|
{
|
|
if(dol->textData[i])
|
|
{
|
|
end = dol->text[i] + dol->textLen[i];
|
|
|
|
if( ((dol->text[i] < 0x81100000) || (end > 0x81300000)) && (end > limit) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Data sections
|
|
for(i=0; i<DOL_MAX_DATA; i++)
|
|
{
|
|
if(dol->dataData[i])
|
|
{
|
|
end = dol->data[i] + dol->dataLen[i];
|
|
|
|
if( ((dol->data[i] < 0x81100000) || (end > 0x81300000)) && (end > limit) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// BSS
|
|
end = dol->bss + dol->bssLen;
|
|
if( ((dol->bss < 0x81100000) || (end > 0x81300000)) && (end > limit) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
Some big transfers requires multiple load times for single apploader part.
|
|
(Limit is set to 64 DVD sectors - WHY?).
|
|
|
|
void LoadBigData(void **addr, s32 *length, s32 *offset, int *part)
|
|
{
|
|
if(SecondTimeForThePart)
|
|
{
|
|
SecondTimeForThePart = FALSE;
|
|
|
|
*addr = SecondTimeAddr;
|
|
*length = SecondTimeLength;
|
|
*offset = SecondTimeOffset;
|
|
*part = SecondTimePart;
|
|
}
|
|
else
|
|
{
|
|
if(*length > 64*2048) // 64 DVD sectors
|
|
{
|
|
// Initiate multiple DVD load.
|
|
SecondTimeForThePart = TRUE;
|
|
|
|
SecondTimeAddr = *addr + 64*2048;
|
|
SecondTimeLength = *length - 64*2048;
|
|
SecondTimeOffset = *offset + 64*2048;
|
|
SecondTimePart = *part;
|
|
|
|
*length = 64*2048;
|
|
*part = 12;
|
|
}
|
|
}
|
|
}
|
|
|
|
Init some OS low memory variables.
|
|
|
|
void InitLoMem(OSLoMem *LoMem)
|
|
{
|
|
LoMem->bi.magic = OS_BOOTINFO_MAGIC; // 0xD15EA5E
|
|
LoMem->bi.version = 1;
|
|
|
|
// Clear debug interface (db/DBInterface.h)
|
|
LoMem->db.bPresent =
|
|
LoMem->db.exceptionMask =
|
|
LoMem->db.ExceptionDestination =
|
|
LoMem->db.exceptionReturn = NULL;
|
|
}
|
|
|
|
Read PAD input buffer (buttons state). If PAD is not polled, return zeroes.
|
|
|
|
void PadRead(int port, u32 *buf)
|
|
{
|
|
// If controller polling enabled
|
|
if(SI_POLL & (0x80 >> port))
|
|
{
|
|
// Read SI input buffer
|
|
buf[0] = SI_CHAN_INBUFH(port);
|
|
buf[1] = SI_CHAN_INBUFL(port);
|
|
}
|
|
else
|
|
{
|
|
buf[0] = buf[1] = 0;
|
|
}
|
|
}
|
|
|
|
------------------------------------------------------------------------------
|
|
|
|
Appendix B: DVD structures.
|
|
|
|
#define DVD_BB2_OFFSET 0x420
|
|
#define DVD_BI2_OFFSET 0x440
|
|
|
|
#define OS_BI2_SIZE 0x2000 // Size of BI2 structure
|
|
|
|
// DVD Boot Block
|
|
typedef struct
|
|
{
|
|
u32 bootFilePosition; // offset of main DOL executable
|
|
u32 FSTPosition; // offset of primary FST
|
|
u32 FSTLength; // primary FST size (in bytes)
|
|
u32 FSTMaxLength; // size of biggest additional FST
|
|
u32 FSTAddress; // FST address in main memory
|
|
u32 userPosition;
|
|
u32 userLength;
|
|
u8 padding0[4]; // reserved, should be 0
|
|
} DVDBB2;
|
|
|
|
FST is File String Table. User area on DVD is where all files are placed.
|
|
On some DVDs, user area is configured wrong. To know correct user area, you
|
|
should parse whole FST and find min and max file offsets.
|
|
|
|
// DVD Boot Info
|
|
typedef struct
|
|
{
|
|
s32 debugMonSize; // size of debug monitor
|
|
s32 simMemSize; // simulated memory size (in bytes)
|
|
u32 argOffset; // command line arguments
|
|
u32 debugFlag; // debug present (set to 3, if CodeWarrior on GDEV)
|
|
u32 TRKLocation; // target resident kernel location
|
|
s32 TRKSize; // size of TRK
|
|
u32 countryCode; // country code
|
|
u32 ? +1C
|
|
u32 ? +20
|
|
u32 ? +24
|
|
u32 dolLimit; // maximum total size of DOL text/data sections (0 - unlimited)
|
|
u32 ? +2C
|
|
|
|
u8 padded0[OS_BI2_SIZE - 0x30]; // reserved, should be 0
|
|
} DVDBI2;
|
|
|
|
Somewhere in +1C, +20 or +24 must present :
|
|
u32 longFileNameSupport; // 1: use long file names, 0: use 8.3 names
|
|
u32 padSpec; // controller version for PADSetSpec
|
|
|
|
------------------------------------------------------------------------------
|
|
|
|
Appendix C: Used OS low memory variables.
|
|
|
|
s32 __OSPhysMemSize AT_ADDRESS(OS_BASE_CACHED | 0x0028);
|
|
s32 __OSDebugMonSize AT_ADDRESS(OS_BASE_CACHED | 0x00E8);
|
|
void* __OSDebugMon AT_ADDRESS(OS_BASE_CACHED | 0x00EC);
|
|
s32 __OSSimMemSize AT_ADDRESS(OS_BASE_CACHED | 0x00F0);
|
|
DVDBI2* __OSBootInfo2 AT_ADDRESS(OS_BASE_CACHED | 0x00F4);
|
|
s32 __OSDOLSize AT_ADDRESS(OS_BASE_CACHED | 0x30D4);
|
|
u16 __OSPADButton AT_ADDRESS(OS_BASE_CACHED | 0x30E4);
|
|
|
|
------------------------------------------------------------------------------
|
|
|
|
Strange Things.
|
|
|
|
What's [+0x148] in apploader data? Its only cleared in Init, but not used.
|
|
|
|
Why FST loading is so strange : if FST placed above DOL, then its loaded after
|
|
DOL, otherwise its loaded before DOL.
|
|
|
|
if(FSTHigh) __OSDOLSize = dolSize + OSRoundUp32B(bb2.FSTLength);
|
|
else __OSDOLSize = dolSize;
|
|
Why so?
|
|
|
|
Why FST is loaded in pieces, if its greater than 64 DVD sectors, but big DOL
|
|
sections are not loaded in same way?
|
|
|
|
Some code is placed after apploader data. Is it garbage or what?
|
|
|
|
------------------------------------------------------------------------------
|
|
|
|
ToDo.
|
|
|
|
Need more BI2 details.
|
|
|
|
Get Zelda apploader and crosscheck it with current reversing. Maybe some
|
|
interesting things will be discovered.
|
|
|
|
Count how many apploader revisions are present today.
|