diff --git a/docs/compilers.md b/docs/compilers.md new file mode 100644 index 0000000000..120aaf43c6 --- /dev/null +++ b/docs/compilers.md @@ -0,0 +1,63 @@ +# Compilers + +Ocarina of Time was written mostly in C, compiled to MIPS machine code. For the +N64 and GameCube versions, all code was compiled with the IDO compiler. For the +iQue Player versions, some of the code (namely libultra, and some game files +such as those related to Chinese text) was compiled with the EGCS compiler instead. + +## IDO + +Ocarina of Time was originally developed on +[Silicon Graphics "Indy"](https://en.wikipedia.org/wiki/SGI_Indy) workstations, +and IDO (IRIS Development Option) was the C compiler toolchain that shipped with +these. Two different versions of IDO were used for Ocarina of Time: IDO 5.3 was +used for some libraries (namely libultra, libleo, and the JPEG library) while +IDO 7.1 was used for the other libraries and all of the "main" game code. + +These Silicon Graphics workstations ran the MIPS-based IRIX operating system, so +the original compiler binaries can't run on modern systems. Originally this +project used [qemu-irix](https://github.com/n64decomp/qemu-irix) (now +unmaintained) to run emulate IRIX on modern systems, but nowadays we use the +more lightweight +[ido-static-recomp](https://github.com/decompals/ido-static-recomp) instead. + +## EGCS + +[EGCS (Experimental/Enhanced GNU Compiler System)](https://en.wikipedia.org/wiki/GNU_Compiler_Collection#EGCS_fork) +was a fork of the GCC compiler. The Linux-based iQue SDK included a patched +version of EGCS release 1.1.2. The original compiler can still run on modern Linux +systems, but we use a +[modified version](https://github.com/decompals/mips-gcc-egcs-2.91.66) +that includes Mac support and a few other minor improvements (such as anonymous +struct/union support). + +This version of the EGCS compiler has a bug where code that indexes into an array member can +fail to compile if the array member is at a large (>= 0x8000) offset in a struct. For +example, when run on the source code + +```c +struct Foo { + char a[0x8000]; + int b[1]; +}; + +int test(struct Foo* foo, int i) { + return foo->b[i]; +} +``` + +the compiler errors with + +``` +Compiler error: src.c: In function `test': +src.c:8: internal error--unrecognizable insn: +(insn 20 18 22 (set (reg:SI 85) + (plus:SI (reg:SI 81) + (const_int 32768))) -1 (nil) + (nil)) +../../gcc/toplev.c:1367: Internal compiler error in function fatal_insn +``` + +In some recompiled files, the game developers had to modify the code to work +around this bug, for example by storing a pointer to the array in a temporary +variable before indexing into it. diff --git a/src/code/z_actor.c b/src/code/z_actor.c index 0626f05de2..37a370557d 100644 --- a/src/code/z_actor.c +++ b/src/code/z_actor.c @@ -2570,7 +2570,7 @@ void Actor_Draw(PlayState* play, Actor* actor) { gSPSegment(POLY_OPA_DISP++, 0x06, play->objectCtx.slots[actor->objectSlot].segment); gSPSegment(POLY_XLU_DISP++, 0x06, play->objectCtx.slots[actor->objectSlot].segment); #else - // Workaround for EGCS bug + // Workaround for EGCS internal compiler error (see docs/compilers.md) slots = play->objectCtx.slots; gSPSegment(POLY_OPA_DISP++, 0x06, slots[actor->objectSlot].segment); gSPSegment(POLY_XLU_DISP++, 0x06, slots[actor->objectSlot].segment); diff --git a/src/code/z_kanfont.c b/src/code/z_kanfont.c index 985e2d52fa..fa038276fe 100644 --- a/src/code/z_kanfont.c +++ b/src/code/z_kanfont.c @@ -119,7 +119,7 @@ void Font_LoadOrderedFont(Font* font) { PRINTF("msg_data=%x, msg_data0=%x jj=%x\n", font->msgOffset, font->msgLength, len); - // Workaround for EGCS bug + // Workaround for EGCS internal compiler error (see docs/compilers.md) msgBufWide = font->msgBufWide; fontBufIndex = 0; for (codePointIndex = 0; msgBufWide[codePointIndex] != MESSAGE_WIDE_END; codePointIndex++) { diff --git a/src/code/z_sram.c b/src/code/z_sram.c index d7d453c9aa..0531e29952 100644 --- a/src/code/z_sram.c +++ b/src/code/z_sram.c @@ -853,7 +853,7 @@ void Sram_InitSave(FileSelectState* fileSelect, SramContext* sramCtx) { #if !PLATFORM_IQUE gSaveContext.save.info.playerData.playerName[offset] = fileSelect->fileNames[fileSelect->buttonIndex][offset]; #else - // Workaround for EGCS bug + // Workaround for EGCS internal compiler error (see docs/compilers.md) u8* fileName = fileSelect->fileNames[fileSelect->buttonIndex]; gSaveContext.save.info.playerData.playerName[offset] = fileName[offset];