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 nullsarg
&larg
. Ends Proc.0001
- Name Instruction: sets thename
field in the Proc State tolarg
.0002
- Call Routine: calls routine (pointed to bylarg
) with the following signature:void routine(ProcState* state);
0003
- Set Cycle Routine: setsonCycle
field in the Proc State tolarg
and yield.0004
- Set End Routine: setsonEnd
field in the Proc State tolarg
.0005
- Start Child Proc: starts a new Child Proc from code pointed bylarg
.0006
- Start Blocking Child Proc: starts a new Blocking Child Proc from code pointed bylarg
and yield.0007
- Start Proc (Bugged): starts a new Proc from code pointed bylarg
in Tree of index represented by thesleepTime
field in the Proc State.0008
- Block Until Running: if a there is a Proc running Code pointed bylarg
, do nothing. If not: donesn’t advances and yields.0009
- End All Of: Ends all Proc running Code pointed bylarg
.000A
- Break Loop for All Of: for each Proc running Code pointed bylarg
: setsonCycle
field in the Proc State to null.000B
- Label: Marks target Instruction for a Goto Instruction (the Label Index is defined bysarg
).000C
- Goto: SetscodeNext
field in Proc State to the pointer to the first Label Instruction whose Label Index equals this Instruction’ssarg
(search begins at Instruction pointed by thecodeStart
in Proc State)000D
- Jump: SetscodeNext
field in Proc State to the value oflarg
.000E
- Sleep: SetssleepTime
field in Proc State to the value ofsarg
. Ifsarg
is non-zero, setsonCycle
field in Proc State to the Special Sleeping Cycle Routine. Yields.000F
- Set Mark: setsmark
field in Proc State to the value ofsarg
.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 thestatebits
field in Proc State (I have no idea what this does).0013
- No Op: does nothing.0014
- Call Routine While: calls routine (pointed to bylarg
). 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 bylarg
). 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 bylarg
) with its first argument defined bysarg
. 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 Instruction000B
(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