eaguru.guru

WIP display list trickery

i've been sporadically working on a widescreen Ocarina of Time hack in the last few weeks. this is different from an emulator hack, as it handles the rendering at a higher level, and (in theory) will work on a real N64.

title screen

(all game screenshots were originally rendered in 640x480 or 320x240, and then stretched to be widescreen in 853x480. until this site gets fancier, you may want to open these images in new tabs to see them in their full resolution)

technical mumbo jumbo

widescreen is accomplished by manipulating microcode commands in the master display lists, just before they're sent out to be rendered. but what does any of that mean?

DLists run-down

the N64 boasts a "reality" coprocessor that the CPU feeds microcode commands to. basically, the main processor writes out commands for a separate chip to follow, that said chip then uses to create a pretty picture on your TV screen. we call a collection of these commands a display list: sometimes DList for short.

microcode commands often use pointers when the data required is larger than the seven bytes available in the command bytecode itself. thus, models and textures, and also matrices and viewports, are passed by pointers. primitive 2D rendering parameters will fit directly into the bytecode. as a result, there are many methods required to procure and process microcode.

microcode is capable of basic subroutines by means of the 0xDE (G_DL) command. one form of this command stores a return address, such that it can be returned from, not unlike calling a function without arguments. this feature is used by the HUD, text boxes, and some special effects.

master DLists

there's three different master display lists in the game [that i've been able to identify] containing all(?) the commands necessary to render the next frame.

the first master DList β€” first referring to the order they appear in RAM β€” will contain all the commands to render the HUD elements. essentially anything that draws "on top" of the screen will be written to here, or called from here. despite being the first DList referred to in RAM, this DList is jumped to last β€” more on this later.

the second is what's responsible for rendering the game world; all the fancy 3D objects are written here. there's also inline subroutines that are written, jumped over, and eventually called later.

the third is… uh, i don't quite recall, but i know that it eventually jumps to the first master display list, and that's the important part.

since it's possible to create subroutines inline, the game uses this method to defer various effects in the second master DList to be drawn later by the first.

confused yet? here's the tl;dr: master display list #2 jumps to #3, which then jumps to #1, which then calls pieces of #2 that were deferred. still confused? yeah, me too.

update: mzxrules has since fleshed out this rendering pipeline stuff.

how the hack works

putting it all together, the hack works by taking relevant microcode commands and recalculating/reconstructing their data to be suitable for stretching to a 16/9 resolution. in other words, it squashes 2D textures into place and expands the viewports of 3D renders.

the hack iterates over the first master DList, and recurses into any 0xDE commands. the recursion is important, as this is how text and the title screen is rendered.

this iteration could cause additional lag, since it's just more code to run every frame, but i haven't run into any such issues yet.

the sign outside hyrule castle

the hack also changes four lines of code to adjust the resolution the main game is rendered at. there's three places where this data is loaded, but only the last (in terms of code flow) should be modified to allow for proper rendering. editing one of the other two loads will result in a widescreen viewport with bad culling (half-rendered polygons), or the screen position being offset.

it's probably possible to edit the second master display list for the same effect, but the current method doesn't require any extra iteration.

as an aside, i do wonder if these functions were adjusted to allow the screen-shrinking effect in Majora's Mask when the days come to an end.

alternative methods

a widescreen hack could be done by modifying the code generating the DLists in the first place. this would be faster than iterating over the already-generated DLists, but as i found out it's a pain to track down each relevant instance of this microcode generation.

also, modifying each instance of code is less appealing for hack authors that may just want a togggleable widescreen mode, regardless of what modifications to the HUD they've already made.

native resolution

angrylion title screen

using angrylion's video plugin, we can see how widescreen functions on an N64 with its native 320x240 resolution. for the record, most emulators (including Virtual Console) default to 640x480.

angrylion hyrule field sign

uh, not the prettiest sight ever. the text gets so scrunched up that it becomes difficult to read. besides that, it functions fine.

known issues

in the hack's current state, there's a handful of problems. these issues shouldn't take much to fix, but i take my sweet ass-time doing things.

z-target reticle error

the Z-targetting reticle doesn't quite line up with its targets. i'm circling around the rock to exaggerate the effect here.

file select screen error

the informative button text on the file-select screen isn't yet adjusted for widescreen.

and sometimes, there's a nauseating screen-jiggling effect, perhaps caused by recalculating the same viewport data twice on every other frame. i won't show you that.

i've been told the hack causes texture corruption when run on a real N64. i'm not too sure about what would be causing this.

notes

source code is in the usual place, in its usual spreading across various files in various directories. i've since moved all the assembly files into one directory, joy!

RAM

for the Debug ROM, you can find the relevant values at the following addresses:

80168930: master DList table

first row: HUD (80168930)
second row: game world (80168940)
third row: miscellaneous (80168950)

first word of a row: maximum size of DList?
second: address of start of DList.
third: address of end of DList.
fourth: unknown address.

cheat code to extend main viewport to 16:9
(you may need to save and reload a savestate)
810AAB90 240B
810AAB92 00D2
810AAB94 240C
810AAB96 001E
810AABA8 2409
810AABAA 0140
810AABAC 240A
810AABAE 0000

Master DList Table seen in Nemu64

if you know C, you may find this pseudo-code more interesting:

typedef struct {
    uint32_t maxsize; // probably
    uint_ptr start;
    uint_ptr end;
    uint_ptr unknown;
} MasterDList;

typedef struct {
    MasterDList first; // HUD, stuff that goes on top
    MasterDList second; // the bulk of the 3D rendering
    MasterDList third; // misc.
} DListTable;

// there's a more roundabout way of finding ctxt,
// but this generally holds up:
GlobalContext *ctxt = 0x80212020;
void *stuff = *ctxt;
DListTable *table = stuff + 0x2A8;