[GUIDE/DOC][ASM] "Procs" (or 6Cs, coroutines, threads, fibers, funky structs, whatever)


#1

Procs (aka 6Cs, coroutines, threads, fibers, funky structs and all of that)

6Cs? more like “sexies” I am right?

I agree, 6Cs are the best. So are coroutines & threads & funky structs & fibers. In fact, in the context of GBAFE Hacking, those are all different names I heard reffering to the same thing: the uhh… well let’s call them “Procs”…

Yes, “6Cs” for circles* & Zane, “funky structs” for Tequila, “coroutines” for Tiki/laqieer*, “threads” for Brendor*, “fibers” for myself (before switching to calling them 6Cs too)… So why “Procs” all of a sudden?

Well in the prototype build of FE8, there’s a bunch of debug strings that hold interesting information. One of which was “PROCINST OVERFLOW”, and another was “proc.c”… And it so happens that those are used in an assertion failure call… In a function that’s core to the whole 6C thing.

Not only that, but there’s a bunch of Proc names (We’ll get to that) that have “PROC” in them (Most of them I found are related to battle initiation but details).

This is all a theory, but given the evidence I’d guess that the original GBAFE developement team called those constructs something that looks like “Procs”.

Anyway all of that to justify me using the name “Proc” (which should probably be “proc” or “PROC”) for those things.

Anyway, in this doc/guide format that you’re probably not surprised to see from me anymore, I will try to explain what “Procs” are and why they are so essential to the core of the GBAFE engines.

*: I may or may not have misremembered who used what term. Sorry if I did.

Introduction: A Proc

I know from experience that trying to explain the concept in a therical way is kind of hard, so let’s proceed with an example.

Our example: popups. By that I mean the window thing that shows up when you get something.

Yes that thing.

Well this thing, along with pretty much everything else in the game, is managed by a Proc.

You may have already guessed that from some of the names it has been given, but a Proc shares a lot of similarities with the concepts in computing of a thread and/or a coroutine/fiber. It is a sequence of code that can be run in parallel to others.

This means that before being in parallel to anything, Procs represents code execution. So what is that code? Is it ASM? Well, some of it, but not all. There are two parts in a Proc’s code:

  • Small bits of ASM, which are just regular routines. No fancy “multiple entry points” or “yielding in the middle” tricks. It’s just good old regular routines that execute from begin to end each time they’re called.
  • The “Proc code”. This is where things become parallelized. Those are sequences of simple instructions (usually involving a single call to one of the “ASM bits” from before) that are interpreted by what we will call the “Proc Code Interpreter” (more on that later).

What’s about this parallel stuff? What does it mean? Well, this means that while the popup Proc is running, other Procs, such as (for example) the Event Engine (yes the event engine is managed by a Proc too) are also running.

I say “while” but they aren’t really running simultaneously. What really happens is that they really quickly are switching execution between each other (and they do this at specific points in Proc execution, when encountering specific Proc Code Instructions. This already makes Procs closer entities to coroutines or fibers than to threads).

Anyway, let’s come back to the code. I said that Procs represent executing code, so where can we find that code?

Well, it’s probably obvious, but each Proc type has its own Proc Code somewhere in the ROM. For example, popups have their code at address 0x085921C8 (FE8U)… And it looks like that:

04 00 00 00 F9 13 01 08
02 00 00 00 6D 11 01 08
0E 00 0A 00 00 00 00 00
02 00 00 00 9D 11 01 08
02 00 00 00 F5 11 01 08
0E 00 00 00 00 00 00 00
02 00 00 00 11 12 01 08
02 00 00 00 71 12 01 08
03 00 00 00 C1 13 01 08
02 00 00 00 39 12 01 08
0E 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00

… Yeah it doesn’t tell us much… Or does it? We are able to spot some kind of pattern: For each 8 bytes, the first 4 seem to encode a single small number, sometimes 2, and the next 4 are encoding a pointer most of the time.

Now what if I showed you this:

04 00 00 00 F9 13 01 08 ; set end handler to asm at 080113F9
02 00 00 00 6D 11 01 08 ; call asm at 0801116D
0E 00 0A 00 00 00 00 00 ; wait 10 cycles
02 00 00 00 9D 11 01 08 ; call asm at 0801119D
02 00 00 00 F5 11 01 08 ; call asm at 080111F5
0E 00 00 00 00 00 00 00 ; wait 0 cycles
02 00 00 00 11 12 01 08 ; call asm at 08011211
02 00 00 00 71 12 01 08 ; call asm at 08011271
03 00 00 00 C1 13 01 08 ; call asm at 080113C1 repeatedly
02 00 00 00 39 12 01 08 ; call asm at 08011239
0E 00 00 00 00 00 00 00 ; wait 0 cycles
00 00 00 00 00 00 00 00 ; the end

Whoah. Everything makes perfect sense now (not really). Of course that’s really because I told you what did what, but you probably can now see some of the links between the data and what it means: 02 is probably related to calling asm, and 0E for waiting. the second word is mostly used for pointers, and 00 00 00 00 00 00 00 00 probably means “the end”.

Well, I’ll go over in better detail on what does what soon, but for now that’s already big help for understanding what that Proc Code is.

The Proc Tree(s)

Ok so I told you that Procs run in parallel. So how to you visualize it? If you didn’t read the name of this section you probably would visualize it like the process list from the task manager.

Yeah like that.

(Note: yes I do know that child processes are a thing and that some kind of process arborescence exist on most systems, but we’re talking visualization here. Besides Procs take this concept on another level anyway)

But you probably did read the name of this section and so you probably are expecting me tell you that Procs are agenced in a tree.

So here I go: Procs are agenced in a tree.

Here’s an example visualization of a Proc Tree:

(I’ll tell you how to get that later just bear with me a sec)

In this tree, we the have root, then its children Proc (of “type” named E_BMAPMAIN) and finally its’s children’s children Proc (of “type” named E_PLAYERPHASE).

So yeah, each Proc has a Parent, and potentially multiple Siblings and Children. Neat, but what does it mean for a Proc to be in a tree?

Well, one of the first thing it means is that when a Proc ends (when it reaches the end of its proc code), it will also forcefully end (terminate) all of its children. When a Proc ends, it stops existing (in practice it means that its state is freed and its parent & siblings linked together accordingly, all of which we’ll get to).

Another thing is that some children can be blocking. This means that as long as the child Proc is “alive”, the parent is blocked (this means its Proc Code doesn’t get executed).

A last thing that’s noteworthy is that the position of a Proc in the tree is directly related to when its executed within a proc cycle:

  • A parent is executed before any of its children.
  • Siblings are executed from youngest to oldest.

Ok so what about that “root” Proc? Well it isn’t a Proc (and because of that some people may call me out because I called the Proc Tree a Tree while the root of the tree isn’t of the same type than the rest of the elements). In practice it’s just reffered as a number. But that number isn’t just a “number”. It’s what I’ll call the tree identifer.

Yes because a tree has to identify itself. This is because there isn’t just one Proc Tree. There’s a whole forest of them! Well there’s room for about 8 so that’s not a very big forest but wikipedia tells me it’s still a forest so here you go. Here’s the entire forest in the context of the single tree I showed earlier:

(You can get that neat display thing by running one of my lua scripts on a lua-capable emulator)

And when the position you have in a tree dictates when your Proc get executed within a Proc cycle, which tree your Proc is in dictates when the Proc cycle itself gets executed.

In other words, each tree is executed at different points in the main game loop (with on exception, since it’s executed elsewhere). Here’s a quick summary of the order trees (and other things) are executed during the main game loop:

  • During The Main Game: (FE8U:080152F4)
    • Key Status is updated
    • higher level OAM Buffer is cleared
    • Tree #1 is executed
    • If Game Logic isn’t Locked:
      • Tree #2 is executed
    • Tree #3 is executed
    • Tree #5 is executed
    • higher level OAM Buffer (lower part) is copied to the lower level OAM Buffer
    • Tree #4 is executed
    • higher level OAM Buffer (higher part) is copied to the lower level OAM Buffer
    • Reads VerticalCounter_LY_ for some reason
    • VBlankIntrWait
  • During Battle Animations: (FE8U:0804FE40)
    • Key Status is updated
    • Sometimes not the follwing: (FE8U:0804FEE4)
      • higher level OAM Buffer is cleared
      • clears some sound-related boolean
      • If Game Logic isn’t Locked
        • Tree #2 is executed
      • Tree #3 is executed
      • Tree #5 is executed
      • higher level OAM Buffer (lower part) is copied to the lower level OAM Buffer
      • Tree #1 is executed
      • AIS Gets Executed (Battle Animations)
      • AIS Special Commands Gets Executed
      • Tree #4 is executed
      • idk battle anim stuff maybe
      • higher level OAM Buffer (higher part) is copied to the lower level OAM Buffer
    • stuff related to special battle Procs
    • Reads VerticalCounter_LY_ for some reason
    • VBlankIntrWait

What we can learn from that is that I still know nothing about how Battle Animations work Trees #1 to #5 are getting executed during the main loop, and in a predictable order. The only real oddity is that Tree #1 gets executed at differents points during the Main Game and during Battle Animations… My guess is that is was intended to be used for setting up AISs? I don’t see Tree #1 executed all that much. Tree #2 & #3 are by far the most commonly used for general applications. Tree #4 & #5 seem to be used specificly for messing with the higher level OAM Buffer.

Tree #2 is particularly interesting since we can see that it is conditionally executed. Tree #2 is where very game centric Procs are usually located, such as “phase managers” (Phase as in Player/Enemy Phase).

I’ll tell you now: even if Tree #6 & #7 technically exist (as in, you can starts Procs in those), they don’t get executed. I guess you could do it yourself if you wanted to (why tho?), but still they don’t have any use in the game as is.

This leaves us only one to figure out: Tree #0 (yes there is a Tree #0). Well the awnser is the Tree #0 is called on vertical blank (which is at the point where the screen has been fully rendered and we have free access to the VRAM for preparing the next frame’s graphics). It isn’t the only thing done during VBlank, here’s a summary: (FE8U:080152A4)

  • Game Clock is incremented
  • Sound stuff
  • Tree #0 is executed
  • lower level OAM Buffer (priority) is copied to actual OAM
  • If (byte at FE8U:0202BCB0 non-zero):
    • Buffered IO Register values are written to the actual IO Registers
    • BG Maps & Palettes are written to VRAM & Palette RAM
    • Queued Tile Updates are processed and written to VRAM
    • lower level OAM Buffer (regular) is copied to actual OAM
  • More sound stuff

I could go hours on what all of this means but… Maybe later. We’re here for Procs right now.

And our example (the Popup Proc) in all of this? Well, as it is for a lot of other Proc types, the Popup Proc can be made two ways: (ref: FE8U:08011490 "NewPopup")

  • As a child of any Proc, in which case it becomes blocking (in other words, its parent has to wait for the popup Proc to end (aka for the Popup to disappear) before continuing execution).
  • As a “standalone” Tree #3 Proc. It’s parent then becomes the Tree #3 Root.

The Proc State

So with what we know about Procs, we may start to understand why they were called “threads” or “coroutines”… But why “funky structs”? And why “6C”?

Well those so-called “funky structs” or “6C structs” are what I’ll start calling Proc State Structs or just Proc States. Those are actual structures located in memory that hold information about a Proc (it’s State).

Since you know that Procs are agenced in a tree, you probably expect there to be informations about the Proc’s “family”. Since you know that Proc Code is interpreted, maybe you figure there should be information about where the next instruction is, and so on. All of this information is stored in what I’ll call the Proc State Header.

But there’s also more, and this is where (I assume) the funky part came from: Most of the Proc State Struct’s available space is reserved for use by the ASM called by the Proc Code. In other words it’s kinda like a persistant Stack Frame, shared (or not) between the different routines that are handling one particular Proc type. Lets call this the Proc State Body.

It’s “funky” because even if one part of the Proc State (the Header) sees consistent use accross all Procs, the other part of it (the Body) has a different layout for each different Proc Type.

Anyway, here’s a complete layout of the Proc State:

+00 word:  codeStart @ Proc Code Pointer: Start
+04 word:  codeNext  @ Proc Code Pointer: Next Instruction
+08 word:  onEnd     @ Function Pointer: Called on Proc end
+0C word:  onCycle   @ Function Pointer: when non-null, called on Cycle instead of Next Instruction
+10 word:  name      @ C-String Pointer: Name (unused in game)
+14 word:  parent    @ Proc State Pointer: Parent State (If Parent is Root, then Tree Index (0-7))
+18 word:  child     @ Proc State Pointer: Oldest Child State (null if none)
+1C word:  previous  @ Proc State Pointer: Previous Sibling (sorted by age) (null if none)
+20 word:  next      @ Proc State Pointer: Next Sibling (sorted by age) (null if none)
+24 short: sleepTime @ Integer: Time left sleeping
+26 byte:  mark      @ Integer: "Mark"
+27 byte:  statebits @ Bitfield: Various Flags
+28 byte:  lockCount @ Integer: Number of times Proc has been "blocked"
+29 to 6C: (Body)    @ Free

And there it is, you all saw it (yes you did don’t lie): 6C. The Proc State’s size is 0x6C bytes, 0x29 of which are reserved for the Header, the rest is free to be used however the routines called by the code feel like.

Back to our example… How is its (the popup Proc) Body layed out? Well, here it is:

+2C word:  popupDefinition @ pointer
+30 word:  timer           @ when reaches 0, closes popup, if negative, waits for button press
+34 byte:  tileX           @ x position in tiles
+35 byte:  tileY           @ y position in tiles
+36 byte:  windowStyle     @ popup windows border style
+37 byte:  tileX2          @ twice?
+38 byte:  tileY2          @ twice?
+39 byte:  tileWidth       @ width in tiles
+3A byte:  tileHeight      @ height in tiles (fixed 3?)
+3B byte:  textColor       @ text color index
+3E short: iconIndex       @ icon index if any
+40 short: iconTileIndex   @ icon OBJ tile index
+42 byte:  iconPaletteId   @ icon OBJ palette index
+44 byte:  iconX           @ icon X?
+46 short: gfxLength       @ length of the whole thing?
+48 short: soundIndex      @ sound index to play

Of course this is all still what I got from Reverse Engineering the game, I don’t know for sure if any of this is true. This is also why there’s question marks to be found in this. Please understand.

The Proc State’s location

So we know what a Proc State is… But do we know where it is? Well, their actual location isn’t really important in most applications, since we usually interract with them through pointers returned by routines from the Proc API (We’ll get to it eventually).

But still knowing how they’re layed out may be a good thing to know to understand how all this mess works.

All (possible) Proc States are stored in a contiguous array, located at a fixed point in memory (FE8U:02024E68), capable of holding up to 0x40 Proc States (the amount of memory used by the array is thus 0x40*0x6C=0x1B00 bytes).

When a Proc is started, one “slot” of this array is automatically “reserved” for holding this Proc’s Proc State, and when a Proc ends, the “slot” is “freed” for another to claim it later.

I’m afraid there isn’t much more than that to it. There’s this whole thing about the allocation pointer array that I don’t really get to be quite honest, but it really isn’t that important (just let the internals stay internal).

The Proc Code

Now that we know, roughly speaking, what a Proc is, let’s take a closer look at its Code. Remember the popup code we went on earlier? Well lets look at it again and try to analyse it in better detail:

04 00 00 00 F9 13 01 08 ; set end handler to asm at 080113F9
02 00 00 00 6D 11 01 08 ; call asm at 0801116D
0E 00 0A 00 00 00 00 00 ; wait 10 cycles
02 00 00 00 9D 11 01 08 ; call asm at 0801119D
02 00 00 00 F5 11 01 08 ; call asm at 080111F5
0E 00 00 00 00 00 00 00 ; wait 0 cycles
02 00 00 00 11 12 01 08 ; call asm at 08011211
02 00 00 00 71 12 01 08 ; call asm at 08011271
03 00 00 00 C1 13 01 08 ; call asm at 080113C1 repeatedly
02 00 00 00 39 12 01 08 ; call asm at 08011239
0E 00 00 00 00 00 00 00 ; wait 0 cycles
00 00 00 00 00 00 00 00 ; the end

First, let’s stop with those single byte packs and start mixing things together. Instructions are 8 bytes long: 2 shorts, then 1 word, so lets make this apparent:

0004 0000 080113F9 ; set end handler to asm at 080113F9
0002 0000 0801116D ; call asm at 0801116D
000E 000A 00000000 ; wait 10 cycles
0002 0000 0801119D ; call asm at 0801119D
0002 0000 080111F5 ; call asm at 080111F5
000E 0000 00000000 ; wait 0 cycles
0002 0000 08011211 ; call asm at 08011211
0002 0000 08011271 ; call asm at 08011271
0003 0000 080113C1 ; call asm at 080113C1 repeatedly
0002 0000 08011239 ; call asm at 08011239
000E 0000 00000000 ; wait 0 cycles
0000 0000 00000000 ; the end

Much better. From that we can already kinda see what is what without having to squint. Now let’s put names on each of those parts.

The first short represents somewhat obviously the instruction type. It’s a number that has a very simple significance: it’s what index in the Code Interpretation Table to look for when handling the instruction.

The Code Interpretation Table (FE8U:085879D8) is an array of 26 (0x1A) pointer to routines that share the following C-Style signature:

int handle_instruction(ProcState* state);

(For those not versed in C: this means the routines takes one argument (a pointer to a ProcState) and return an integer)

The role of the argument is kinda obvious: it’s to know what Proc we’re talking about. In the Proc State is all the informations we need (including the pointer to the next/current proc instruction).

The role of the return value, however, is less obvious. See if the routine returns 0, then the instruction becomes yielding, which means that it marks the end of the current Proc Cycle: the Interpreter stops interpretation of this Proc and starts the next one.

I’ll list in detail what each individual code does soon but for now lets go back to names: the first then short becomes the instruction type.

The other two parts are less obvious. You may have guessed that their meaning depend on the instruction type. So to generalize we’ll simply call them the short argument (“sarg”), and the long argument (“larg”).

To conclude, we can lay out each Proc Code Instruction the following way:

+00 short: type @ instruction type
+02 short: sarg @ short argument
+04 word:  larg @ long argument

Ok so now it’s time to dump a big old list of codes for you to not learn by heart:

  • 0000 - End Instruction: usually comes with null sarg & larg. Ends Proc.
  • 0001 - Name Instruction: sets the name field in the Proc State to larg.
  • 0002 - Call Routine: calls routine (pointed to by larg) with the following signature:
    • void routine(ProcState* state);
  • 0003 - Set Cycle Routine: sets onCycle field in the Proc State to larg and yield.
  • 0004 - Set End Routine: sets onEnd field in the Proc State to larg.
  • 0005 - Start Child Proc: starts a new Child Proc from code pointed by larg.
  • 0006 - Start Blocking Child Proc: starts a new Blocking Child Proc from code pointed by larg and yield.
  • 0007 - Start Proc (Bugged): starts a new Proc from code pointed by larg in Tree of index represented by the sleepTime field in the Proc State.
  • 0008 - Block Until Running: if a there is a Proc running Code pointed by larg, do nothing. If not: donesn’t advances and yields.
  • 0009 - End All Of: Ends all Proc running Code pointed by larg.
  • 000A - Break Loop for All Of: for each Proc running Code pointed by larg: sets onCycle field in the Proc State to null.
  • 000B - Label: Marks target Instruction for a Goto Instruction (the Label Index is defined by sarg).
  • 000C - Goto: Sets codeNext field in Proc State to the pointer to the first Label Instruction whose Label Index equals this Instruction’s sarg (search begins at Instruction pointed by the codeStart in Proc State)
  • 000D - Jump: Sets codeNext field in Proc State to the value of larg.
  • 000E - Sleep: Sets sleepTime field in Proc State to the value of sarg. If sarg is non-zero, sets onCycle field in Proc State to the Special Sleeping Cycle Routine. Yields.
  • 000F - Set Mark: sets mark field in Proc State to the value of sarg.
  • 0010 - Halt: doesn’t advance and yields (preventing any following instructions to be executed at all).
  • 0011 - Ensure Unique (Weak): if any Proc exists running the same Proc Code as this one, ends this Proc (ensuring this Proc only runs if it’s the only one running its Proc Code).
  • 0012 - ???: sets bit 3 in the statebits field in Proc State (I have no idea what this does).
  • 0013 - No Op: does nothing.
  • 0014 - Call Routine While: calls routine (pointed to by larg). If the routine returns 1, doesn’t advance and yeilds. Routine signature:
    • int routine(ProcState* state);
  • 0015 - No Op: does nothing. Note: in FE7 and FE6, this seems to be the instruction replacing the Name Instructions, as those aren’t present in the Proc Codes of those games.
  • 0016 - Call Routine (can yield): calls routine (pointed to by larg). If the routine returns zero, yields. routine signature:
    • int routine(ProcState* state);
  • 0017 - Ensure Unique (Strong): if any Proc exists running the same Proc Code as this one, ends the other Procs (ensuring this Proc is the only one running its Proc Code).
  • 0018 - Call Routine with argument (can yield): calls routine (pointed to by larg) with its first argument defined by sarg. If the routine returns zero, yields. routine signature:
    • int routine(short sarg, ProcState* state);
  • 0019 - No Op: does nothing. Note: this Instruction Type shares its Handling routine with Instruction 000B (Label).

Note: when I say “yield”, it means that it marks the end of the current Proc Cycle. In other words, the code won’t continue running until the next one (which usually means until next frame).

Note: when I say “doesn’t advance”, it means that the next Proc Code Instruction stays the current instruction. In other words, the same instruction will be executed again.

Ok, whew, that’s a lot to take in I’ll admit. There may be some new things popping here and there (Labels? Sleeping?) that we are going to go over in more details soon.

Note that this panoply of available instructions is more than enough for designing any kind of Proc we need. This is mostly achieved by the fact that we can very easily delegate the more complex and precise behaviors to assembly, and let the Proc Code handle the general control flow.

“Loops” (cycle handlers)

This is about codes 0003 (Set Cycle Routine) and to some extent 000E (Sleep). I say in their descriptions that they set the onCycle field in the Proc State. This routine pointer is important, as it will be called instead of the instruction interpreter on a Proc Cycle.

This means that after you set the cycle handler with 0003, instead of interpreting the next instruction, that cycle handler will be called, and it will be every cycle until the onCycle field in the Proc State gets cleared.

This is useful for when you need to be doing the same thing every frame, such as, for example, writing OAM Data to one of the OAM Buffers (that get cleared each frame).

It is also useful for when you’re waiting for a few very specific events, such as a particular key press, even if I’d argue that this is better done through code 0014 (Call Routine While).

The C-Style signature of cycle handlers is the following:

void routine(ProcState* state);

Note: cycle handlers are also called after Instruction Interpretation when the onCycle field in the Proc State is non-null. This means that if you use code 0003, the handler will be called immediately.

Sleeping

This is about code 000E (Sleep, fittingly). In effect, this halts the Proc for a given number of cycles (specifically the number given by sarg). This is useful for example for timing different graphical effects in a spell animation.

The way sleeping works is fairly simple: when a 000E instruction is executed, it sets the cycle handler to the Special Sleeping Cycle Routine (FE8U:08003290). This routine simply decrements the value of the sleepTime field in the Proc State until it reaches zero, at which point it simply clears the cycle handlers (onCycle field in Proc State), allowing the next instructions to be interpreted next cycle.

But what about 000E with sarg=0? Does it sleep 0 cycles? I said it wouldn’t set the cycle handler in this case so… does it do nothing? Well no. 000E is unconditionally yielding, so it would still “sleep” for… One cycle. But does it mean that with sarg=1 it sleeps 2 cycles? No it still will sleep one cycle… So what’s up?

Well whether you set sarg to 0 or 1 doesn’t change the end result. In both cases, the Proc would continue execution next frame. The only difference is that since with sarg=0 there is no cycle handler set, it is slightly less CPU-time-consuming (no extra routine call and execution, just good old yield).

Note: in my various utility libraries that mean to help write Proc Code, I usually alias the “Sleep 0 cycles” instruction to “Yield”, since this is mostly the effect wanted from this instruction.

Labels

Code 000B allows you to set a label (or “goto target”), and 000C allows you to (unconditionally) go to a certain label (as in, set the next instruction pointer to its location).

For example, the following sequence:

000C 0001 00000000 ; Goto 1
0010 0000 00000000 ; Halt
000B 0002 00000000 ; Label 2
000C 0003 00000000 ; Goto 3
0010 0000 00000000 ; Halt
000B 0001 00000000 ; Label 1
000C 0002 00000000 ; Goto 2
0010 0000 00000000 ; Halt
000B 0003 00000000 ; Label 3
0000 0000 00000000 ; End

Will see the “cursor” jump to label 1, then 2, then 3 (and never executing any of the 0010 codes, that would halt the Proc undefinitely).

This is useful when you want to setup loops containing multiple Instructions, for example, see this Proc Code (FE8U:0859D908):

0001 0000 080D7EE4 ; Name "MAPTASK"
0017 0000 00000000 ; Ensure Unique (Strong)
000F 0001 00000000 ; Mark 1
000E 0000 00000000 ; Yeild
000B 0000 00000000 ; Label 0
0002 0000 080273A5 ; Call Routine at 080273A5
0002 0000 08030C0D ; Call Routine at 08030C0D
0002 0000 08019D29 ; Call Routine at 08019D29
000E 0000 00000000 ; Yield
000C 0000 00000000 ; Goto 0

Each time it reaches the “end” (Note that there is no actual end instruction here. It’s possible but I’d advice against doing that if you can), it goes back to the label 0, doing its thing all over again. Note that if you don’t yield somewhere in the loop body, you’ll effectively halt the game since it would generate an infinite loop.

But we see that we are quickly limited by the unconditional nature of the 000C (Goto) instruction type. Is there a way to make conditional jumps?

Will you may have already guessed it, but you can also have the Proc Goto through a routine call (that you’d put in one of your own routines, in which you have full control on the flow). I’ll go on that in a little more detail later when I’ll talk about the Proc API, but for example you could have something like this:

void routine(MyProcState* state) {
	if (state->someValue == 0)
		proc_goto_label(&state->header, 0);
	else
		proc_goto_label(&state->header, 1);
}

And have this routine called through a 0002 instruction.

This method is heavily used, for example, by the Player-Controlled Phase Proc (code at FE8U:0859AAD8), where the Proc can be in a lot of different states. It is in those contexts that Procs can be easily considered akin to finite state machines.

To Be Continued


[FE8] (and maybe FE7) Leonarth's ASM thingies
The Grand Library of FEU [UNDER CONSTRUCTION]
[GUIDE/DOC] ASM Hacking in C (with EA)
[DOC][ASM][WIP]Procs: the FE5 version
#2

The Theorical “Proc API”

Of course there’s no such thing as an official “Proc API” that just magically happened to have come with the game. Here I’ll talk about the one that we could define ourselves from what we know (In fact, it already has been done. I’ll be using the names used by the fireemblem8u decompilation project).

There’s a bunch of routines located in the game that were obviously meant to be used (by the original developpers) to handle Procs. I’ll try to list the most interesting ones here (with their FE8U location), and define their utility. I’ll by using C-Style signatures, so here we go:

ProcState* Proc_Create(const ProcCode* code, ProcState* parent);          // FE8U:08002C7C
ProcState* Proc_CreateBlockingChild(const ProcCode* code, ProcState* parent); // FE8U:08002CE0

Those are obviously used to start Procs. You may or may not need the ProcState* pointer for further operations. In the case of Proc_Create, the parent argument can also be number between 0 and 7, in which case the Proc’s parent becomes the root of the tree of the corresponding index.

Note: when starting a proc, the Proc Code starts to get executed immediately! So be careful with that, and add in a Yield instruction if you need to.

ProcState* Proc_Find(const ProcCode* code); // FE8U:08002E9C

Looks for a Proc running the passed Proc Code, and returns a pointer to its State. If no Proc is running this Proc Code, returns a null pointer (aka zero).

void Proc_ForEach(void(*func)(ProcState*));                        // FE8U:08002F70
void Proc_ForEachWithScript(const ProcCode* code, void(*func)(ProcState*)); // FE8U:08002F98
void Proc_ForEachWithMark(int mark, void(*func)(ProcState*));      // FE8U:08002FC0

Note: “script” is the name used by decomp to refer to proc code.

Calls the function pointed by the passed function pointer for all active Procs/for each Proc running the passed Proc Code/for each Proc whose mark match the passed one.

void Proc_Delete(ProcState* state);          // FE8U:08002D6C
void Proc_DeleteAllWithScript(const ProcCode* code); // FE8U:08003078

This ends a Proc/each Proc running passed Proc Code, and all its/their descendants (if any).

void Proc_ClearNativeCallback(ProcState* state);              // FE8U:08002E94
void Proc_ClearNativeCallbackEachWithScript(const ProcCode* code); // FE8U:08003094

Note: “native callback” is the term used by decomp to refer to cycle routines.

This clears the onCycle field in the Proc State of the passed Proc/of each Proc running the passed Proc Code.

void Proc_GotoLabel(ProcState* state, int index); // FE8U:08002F24

Sets next instruction to the first label instruction of index index (search starts for Proc Code Start) (see the subsection in The Proc Code named “Labels” for details). Also clears onCycle.

void Proc_JumpToPointer(ProcState* state, const ProcCode* code); // FE8U:08002F5C

Sets next instruction to the one pointed by code. Also clears onCycle.

Note: a jump doesn’t change the start code pointer, which means that after a jump, things like labels may not work as intended.

void Proc_SetMark(ProcState* state, int mark); // FE8U:08002F64

Sets mark field in the Proc State state to the value of the mark argument.

void Proc_BlockEachWithMark(int mark);   // FE8U:08002FEC
void Proc_UnblockEachWithMark(int mark); // FE8U:08003014

Halts/Resumes all Procs with matching marks. (Useful for pausing entire systems. For example: pausing all map sprites related Procs when entering a stat screen).

void Proc_Run(ProcState* state); // FE8U:08002E84

Executes a Proc Cycle for the Proc, all its descendants (if any) and all its younger siblings (again, if any). This is the routine called for executing entire Proc Trees (by passing the oldest children of its root).

Routine locations in other games

The locations I gave here are the locations of the routines for FE8U, obviously, but don’t panic! We have already located all of those routines also in FE7U, FE7J and FE6 (no FE8J as of now sorry) (big thanks to Teq for doing the FE7U search), and can find all of that and more in my old-ish yet still relevant 6CNotes.txt file, available on the unified docbox.

Let’s make a Proc

Ok so let’s try to put everything we learned in practice and just make a proc.

The Goal: We’ll start our Proc through a simple ASMC. What it will do is simply Fade into dark, Wait for user input (a key press), and Fade out of dark. Another thing to note is that we want the Proc to be a Blocking Child of the event engine proc (I’ll get to how to do that later).

We’ll be doing the thing in ASM because I’m nice. However we will want to make use of some linking ability so grab lyn (or another linker but if you use Event Assembler then you probably want lyn) instead of your prehistoric “Assemble ARM.bat” thing that you go from Kirb’s beginner pack.

Ok so, first thing we’ll set up our Proc Code. Since we aren’t requiring any kind of setup from the ASMCed routine, we don’t have to be scared of early execution, so no need to start with a yield (see my description of the Proc_Create routine).

We want to Fade into dark. If you know how to do that don’t worry I’ll tell you. What you want to know is that this is done through creating another child proc (that will also be blocking). It sounds complicated but in reality there’s just a routine call involved, so a 0002 instruction will suffice.

We want to wait for user input. We could use 0003 (Set Cycle Routine) for that or use 0014 (Call Routine While). I’m opting towards 0014 since this it seems more fitting for a “waiting for a particular event” scenario.

Next we want to Fade out of dark. Same as for fading into, it’s an external Proc made through a routine call that blocks ours.

Finally, the end code.

Here’s what I have:

.thumb

.global MyProcASMC
.type   MyProcASMC, %function

.type   MyProc_FadeIntoDark, %function
.type   MyProc_CheckUserInput, %function
.type   MyProc_FadeOutOfDark, %function

.align
MyProcASMC:
	bx lr

.align
MyProcCode:
	.word 0x0002, MyProc_FadeIntoDark
	.word 0x0014, MyProc_CheckUserInput
	.word 0x0002, MyProc_FadeOutOfDark
	.word 0x0000, 0

.align
MyProc_FadeIntoDark:
	bx lr

.align
MyProc_CheckUserInput:
	mov r0, #0
	bx lr

.align
MyProc_FadeOutOfDark:
	bx lr

.ltorg
.align

Note: I used .word instead of .short, yet it will still work because of how little endianness works & we don’t use instructions requiring sarg.

Now, let’s start the Proc. it won’t do a thing, I’ll admit, we still need to get that done, but let’s start with starting.

As you may have guess before, we are going to be using the Proc_Create routine… The Proc_CreateBlockingChild routine that is! Remember what I said about blocking the event engine? yeah that.

See, unlike what you might think, a routine called by an ASMC does take an argument: a pointer to the Proc State of the Event Engine Proc. So we can simply pass that to the Proc_CreateBlockingChild to make our Proc blocking. Simple!

Here’s my MyProcASMC:

.align
MyProcASMC:
	push {lr}
	
	mov r1, r0         @ argument r1 = parent
	adr r0, MyProcCode @ argument r0 = code (needs to be later in the file, not earlier!)
	
	ldr r3, =(Proc_CreateBlockingChild+1) @ thumb bit!
	bl BXR3
	
	@ we don't need the return value so whatever
	
	pop {r0}
	bx r0

With Proc_CreateBlockingChild defined as the address of the corresponding routine and BXR3 a label to a bx r3 instruction.

We can already see if it works by trying to run it with some remarkable effect… Say let’s return 1 (instead of 0) in MyProc_CheckUserInput. This should softlock the game entirely, since our Proc will never end.

Here’s my lyn output if you can’t or don’t want to use lyn for some reason:

PUSH
ORG (CURRENTOFFSET+$1); MyProcASMC:
POP
{
PUSH
ORG (CURRENTOFFSET+$31); _L0000000000060008:
ORG (CURRENTOFFSET+$4); _L0000000000060009:
ORG (CURRENTOFFSET+$4); _L000000000006000A:
POP
SHORT $B500 $1C01 $A002 $4B0E $F000 $F818 $BC01 $4700
BYTE $02 $00 $00 $00
POIN _L0000000000060008
BYTE $14 $00 $00 $00
POIN _L0000000000060009
BYTE $02 $00 $00 $00
POIN _L000000000006000A
BYTE $00 $00 $00 $00 $00 $00 $00 $00
SHORT $4770 $46C0 $2001 $4770 $4770 $46C0 $4718
BYTE $00 $00 $E1 $2C $00 $08
}

Now add ASMC MyProcASMC in some event and if it softlocks at that point, mission accomplished!

Now, let’s tackle the key input part. It’s fairly easy: see, there’s a struct located at FE8U:02024CC0 (I have docced the details here, and it is also included in the Teq Doq), that contains information about what keys are pressed, which were held, et cetera. Most members mimic the format of the KEYINPUT Register. What we will do is simply read from the “current” field (+04 short) and see if it’s non-zero:

.align
MyProc_CheckUserInput:
	ldr  r0, =key_status_buffer
	ldrh r0, [r0, #0x04] @ current keys held bits
	
	cmp r0, #0
	bne MyProc_CheckUserInput_NonZeroKeyHeld
	
	mov r0, #1
	b MyProc_CheckUserInput_End
	
MyProc_CheckUserInput_NonZeroKeyHeld:
	mov r0, #0

MyProc_CheckUserInput_End:
	bx lr

(Yeah I know I may have my label names a bit too verbose, but that’s why I like have the ability to have namespaces so much).

Anyway assemble, lyn, assemble (now with EA) & run and you should be in the same situation as earlier but now you can press a key to exit the not-so-softlocked-anymore state.

Yay progress! Now for the final touch: fading in and out of dark.

There’s a (well 2, one for fading in and one for fading out) Proc that manages that (actually there’s a variety of Procs that do that, but here we will make use of the ones called by the event engine). Like most Procs (but not ours), they have a dedicated “start this Proc” routine. I’ll name them the start_fade_into_dark (FE8U:08013D08) and the start_fade_outof_dark (FE8U:08013D20) routines. They both take a duration in r0 and a parent Proc State pointer in r1.

So now to write the actual routines:

.align
MyProc_FadeIntoDark:
	push {lr}
	
	mov r1, r0    @ argument r1 = parent
	mov r0, #0x10 @ argument r0 = duration
	
	ldr r3, =(start_fade_into_dark+1)
	bl BXR3
	
	pop {r0}
	bx r0

And do the same with MyProc_FadeOutOfDark (except you use the other routine). Try it out and yay it worked!

However, I’ll tell you the following: Since we are using 0002 to call our routine, and in that routine we effectively halted our Proc, we probably want that this halt takes effect “immediately”. The thing is: we are in the middle of executing the Proc Instruction, and the Interpreter only checks for wether the Proc is active or not before any interpretation. TL;DR we want to yield here. So let’s add those yield instruction in the Proc Code:

.align
MyProcCode:
	.word 0x0002, MyProc_FadeIntoDark
	.word 0x000E, 0
	
	.word 0x0014, MyProc_CheckUserInput
	
	.word 0x0002, MyProc_FadeOutOfDark
	.word 0x000E, 0
	
	.word 0x0000, 0

There. Done. Well done even. We made a Proc, and it works. On that note I think I have done enough. The goal of this document was to teach you what they are and how they work, and at this point I think I’ve done it all.

Final ASM:

.thumb

.set start_proc_blocking,   0x08002CE0

.set start_fade_into_dark,  0x08013D08
.set start_fade_outof_dark, 0x08013D20

.set key_status_buffer,     0x02024CC0

.global MyProcASMC
.type   MyProcASMC, %function

.type   MyProc_FadeIntoDark, %function
.type   MyProc_CheckUserInput, %function
.type   MyProc_FadeOutOfDark, %function

.align
MyProcASMC:
	push {lr}
	
	mov r1, r0         @ argument r1 = parent
	adr r0, MyProcCode @ argument r0 = code
	
	ldr r3, =(Proc_CreateBlockingChild+1) @ thumb bit!
	bl BXR3
	
	@ we don't need the return value so whatever
	
	pop {r0}
	bx r0

.align
MyProcCode:
	.word 0x0002, MyProc_FadeIntoDark
	.word 0x000E, 0
	
	.word 0x0014, MyProc_CheckUserInput
	
	.word 0x0002, MyProc_FadeOutOfDark
	.word 0x000E, 0
	
	.word 0x0000, 0

.align
MyProc_FadeIntoDark:
	push {lr}
	
	mov r1, r0    @ argument r1 = parent
	mov r0, #0x10 @ argument r0 = duration
	
	ldr r3, =(start_fade_into_dark+1)
	bl BXR3
	
	pop {r0}
	bx r0

.align
MyProc_CheckUserInput:
	ldr  r0, =key_status_buffer
	ldrh r0, [r0, #0x04] @ current keys held bits
	
	cmp r0, #0
	bne MyProc_CheckUserInput_NonZeroKeyHeld
	
	mov r0, #1
	b MyProc_CheckUserInput_End
	
MyProc_CheckUserInput_NonZeroKeyHeld:
	mov r0, #0

MyProc_CheckUserInput_End:
	bx lr

.align
MyProc_FadeOutOfDark:
	push {lr}
	
	mov r1, r0    @ argument r1 = parent
	mov r0, #0x10 @ argument r0 = duration
	
	ldr r3, =(start_fade_outof_dark+1)
	bl BXR3
	
	pop {r0}
	bx r0

.align
BXR3:
	bx r3

.ltorg
.align

Final lyn output:

PUSH
ORG (CURRENTOFFSET+$1); MyProcASMC:
POP
{
PUSH
ORG (CURRENTOFFSET+$41); _L0000000000060008:
ORG (CURRENTOFFSET+$10); _L0000000000060009:
ORG (CURRENTOFFSET+$10); _L000000000006000A:
POP
SHORT $B500 $1C01 $A002 $4B1B $F000 $F832 $BC01 $4700
BYTE $02 $00 $00 $00
POIN _L0000000000060008
BYTE $0E $00 $00 $00 $00 $00 $00 $00 $14 $00 $00 $00
POIN _L0000000000060009
BYTE $02 $00 $00 $00
POIN _L000000000006000A
BYTE $0E $00 $00 $00 $00 $00 $00 $00 $00 $00 $00 $00 $00 $00 $00 $00
SHORT $B500 $1C01 $2010 $4B0C $F000 $F812 $BC01 $4700 $480A $8880 $2800 $D101 $2001 $E000 $2000 $4770 $B500 $1C01 $2010 $4B06 $F000 $F802 $BC01 $4700 $4718
BYTE $00 $00 $E1 $2C $00 $08 $09 $3D $01 $08 $C0 $4C $02 $02 $21 $3D $01 $08
}

Glossary

  • Proc: What this document is all about.
    • Proc Code: Sequence of Proc Code Instructions usually located in ROM that get executed during a Proc Cycle, up until either encountering a Yielding Instruction or the End Instruction.
    • Proc Type: Somewhat abstract concept of which a Proc belong to. Different Procs of the same type would share the same Proc State Body layout, and usually run the same Proc Code.
    • Proc Code Instruction: structure present within Proc Code containing 2 shorts and one word of data encoding a command.
      • Yielding Instruction: Any Proc Code Instruction that marks the end of a Proc Cycle.
      • End Instruction: Specific Proc Code Instruction (null instruction type) that marks the end of one Proc’s code.
    • Proc Cycle: Event in which one Proc’s Proc Code Instructions gets executed.
    • Proc Tree: Tree where all Nodes (except the Root) represents Procs.
      • Proc Tree Execution: Event where a Proc Cycle happens for each Proc in a Proc Tree, in a determinable order (Parents before Children, Younger Siblings before Older Siblings).
    • Proc State or Proc State Struct: memory block, of which the size is famously 0x6C, containing information about the State of a Proc.
      • Proc State Header: Common header for all Proc States, containing general information about the Proc’s family and state of execution, among other things.
      • Proc State Body: “Free Space”. The layout of this part of the Proc State is dependent on the Proc Type.

The End

That’s it. Now you know what a Proc is. Or what a 6C is. Or what a funky struct, coroutine, thread or fiber is. You get the idea.

I hope this was useful to someone. As always, if you have spotted something fishy or have questions and/or suggestions, feel free to ask!

Have another nice day! -Stan


#3

what did we do to deserve u


#4

#5

Hi, just thought I’d drop in a bit of the missing history here.

The name “6C” originates with me. I stumbled upon a fair chunk of what you’re calling the “Proc API” while working on the text hax for FEE3 2016 (hard to believe it’s been almost two years), along with noticing the setup of memory structs 0x6C bytes in size. It was part of manually decompiling the existing code to try to figure out how everything worked, and I got into the weeds and my brain was hurting before I realized I totally didn’t need to figure that part out to actually get my changes done, and I could just make sure to preserve this one parameter to the big main text-handling function that I didn’t understand (pretty sure now that it was a pointer to a proc state).

I also had noticed that the first two words in a proc state were pointers with the behaviour you now document, and noticed the 8-byte data structs they refer to - but really had no idea what was going on with them (I had hoped they might have something to do with events, but that didn’t pan out). I think you or someone else suggested that they had some kind of scripting purpose, which made sense, and in my own notes (which I’m not sure if I ever actually shared around) I started referring to the proc code structs as “scripts” and the proc states as “script runners”. (That said, of the names you list, “coroutines” really makes the most sense to me.)

It’s probably also worth noting here that where you describe bytes 0x29 to 0x6C as “free to be used however the routines called by the code feel like”, I would consider it almost certain that 0x29…0x2B are struct padding that would be inaccessible from C without pointer hax, and not intended for use.


#6

an 8 bit integer wouldn’t pad in C, and therefore I’m pretty sure it would use 0x29, and if there’s more of them, 0x2A-0x2B. Don’t quote me on this though


#7

If the header was a separate struct in the original source, then yes: bytes 29-2B would be inaccessible because of struct sizes being aligned 4. But those are still used by a fair amount of procs for their purpose; Which means that it probably couldn’t have been the case. (This is why, for decomp, we use some weird preprocessor thing for the proc header instead).

Since it’s used by vanilla procs all over the place I don’t see why I wouldn’t document them as free too.

Proc script would have been a better name than proc code but I am bad at names ugh.

(Also Kirb yes but that’s irrelevant. Here what would generate padding is the header struct, not the hypothetical following 8bit int).


#8

C will pad within a struct as well as at the end, for alignment.