[DOC][ASM][WIP]Procs: the FE5 version

Reading Stan’s Thread is a prerequisite to reading this one. Come back once you’ve read it. Alright, you ready? Throw most of that knowledge out.

Intro: A (FE5) Proc

Surprise, surprise, these little popup windows are handled with procs in FE5, too. Procs in FE5 are a bit different from the GBAFE ones.

#Proc format

Here’s the format of a proc type:


 +00 word: 2 letter ascii proc name
 +02 word: start routine pointer
 +04 word: on-cycle routine pointer
 +06 word: proc code pointer

There’s something very goofy going on here, though. Each of these pointers can only point to things within the current bank. This’ll be important later. All of this data gets copied into the proc state stuct, so let’s move on to that.

Just like GBAFE procs, FE5 procs have a proc state struct in RAM. Rather than being a contiguous block like GBAFE, each word in a single struct is spaced every 0x20 bytes (There’ll be a visualization of that in a moment, don’t worry). Here’s the layout:


Header:
$053D | $055B - (2 * slot) | Proc Type offset
$055D | $057B - (2 * slot) | Proc Type bank
$057D | $059B - (2 * slot) | Proc name
$059D | $05BB - (2 * slot) | Bitfield
$05BD | $05DB - (2 * slot) | OnCycle offset
$05DD | $05FB - (2 * slot) | Code offset
$05FD | $061B - (2 * slot) | Sleep timer
$061D | $063B - (2 * slot) | Unknown timer

Body:
$063D | $065B - (2 * slot) | These are free to
$065D | $067B - (2 * slot) | be used however the
$067D | $069B - (2 * slot) | proc likes
$069D | $06BB - (2 * slot) | 
$06BD | $06DB - (2 * slot) | 
$06DD | $06FB - (2 * slot) | 
$06FD | $071B - (2 * slot) | 
$071D | $073B - (2 * slot) | 

Unknown:
$073D | $075B - (2 * slot) | copy of header bitfield


Procs are added in reverse order, where the first proc has its type lower word at $055B, the second at $0559, etc. This lower word is used to determine if a proc slot is occupied, and is cleared on the proc’s destruction. There is room for 0x20 procs.

Here’s what this popup looks like in RAM:

This format is pretty readable when there are only a few active.

An important difference between GBAFE procs and FE5 procs is that FE5 procs need no proc code at all. They can mix and match having a start routine, an on-cycle routine, and a proc code (You could have a proc that has none of these, but that’s pretty pointless). Start routines are executed immediately after the proc state is written, and then the on-cycle routine is executed before moving on.

#A little bit about the proc code:

FE5’s proc code isn’t as convenient as GBAFE’s. There are two types of codes, the first being timer numbers. A timer number simply sets the timer in the proc state. They’re a word long.

The other type is rather annoying. GBAFE proc instructions are an index in a table of routines, whereas FE5 instructions are pointers to routines.

Routine pointers are also only a word long. Remember when I said that the pointers in the proc type referred to the bank the type was in? Proc code routine pointers all point to things in bank $82 (mapped). To get around this limitation, these routines usually take parameters like long pointers to other routines. Anyway, each instruction being a short pointer in bank $82 leaves me to hunt down possible routines, so I don’t have a convenient list, yet.

Confused? I’ll have an example ready soon.

This is super WIP and will be updated as I learn things.

7 Likes

Sorry to keep you all on the edge of your seats.

Here’s an example of a really simple proc: Fade Out

proc_FO_82A137 ; $82A137 proc type
	.text "FO"
	.word <>proc_FO_82A137_start ; $82A13F
	.word <>proc_FO_82A137_oncycle ; $82A14D
	.word <>proc_FO_82A137_code ; $82A15B

proc_FO_82A137_start ; $82A13F
	php
	jsl l_func_82A15D ; clear fade-ins if they exist
	sep #$20
	.as
	lda #$FF
	sta @w$033D ; flag for fading
	plp 
	rtl 

proc_FO_82A137_oncycle ; $82A14D
	lda #$0001 ; frame timer
	jsl $80ABC7 ; decrease screen brightness after frame timer in A
	bcc + ; returns carry clear if screen isn't black
	jsl $829D11 ; delete proc state given by index in X (this proc)

+
	rtl

proc_FO_82A137_code ; $82A15B
	.word <>$829DCB ; loop

l_func_82A15D ; $82A15D
	phx
	lda #$8200 ; $82A111 fade-in proc
	sta @b$6E ; $6D is generally long inputs for the proc system
	lda #$A111
	sta @b$6D
	jsl $829CEC ; check if proc exists
	bcc + ; if not, hop down
	txa ; index of fade-in proc state
	lsr a
	jsl $829CB8 ; deletes proc state if exists

+
	lda #$8200 ; another fade-in proc
	sta @b$6E ; same thing again
	lda #$A20C
	sta @b$6D
	jsl $829CEC
	bcc +
	txa 
	lsr a
	jsl $829CB8

+
	plx
	rtl

Let’s break it down section by section.


#Startup

proc_FO_82A137_start ; $82A13F
	php
	jsl l_func_82A15D ; clear fade-ins if they exist
	sep #$20
	.as
	lda #$FF
	sta @w$033D ; flag for fading
	plp 
	rtl 

...

l_func_82A15D ; $82A15D
	phx
	lda #$8200 ; $82A111 fade-in proc
	sta @b$6E ; $6D is generally long inputs for the proc system
	lda #$A111
	sta @b$6D
	jsl $829CEC ; check if proc exists
	bcc + ; if not, hop down
	txa ; index of fade-in proc state
	lsr a
	jsl $829CB8 ; deletes proc state if exists

+
	lda #$8200 ; another fade-in proc
	sta @b$6E ; same thing again
	lda #$A20C
	sta @b$6D
	jsl $829CEC
	bcc +
	txa 
	lsr a
	jsl $829CB8

+
	plx
	rtl

The startup routine here hunts down existing fade-ins and removes their proc states. We don’t want conflicting fades otherwise we wouldn’t get anything done.


#On cycle

proc_FO_82A137_oncycle ; $82A14D
	lda #$0001 ; frame timer
	jsl $80ABC7 ; decrease screen brightness after frame timer in A
	bcc + ; returns carry clear if screen isn't black
	jsl $829D11 ; delete proc state given by index in X (this proc)

+
	rtl

Essentially, every frame it’s going to set the screen brightness one setting lower. On reaching full darkness, $80ABC7 will return with the carry flag set, and it’ll then be able to run $829D11, which’ll destroy the fade out proc state. It’s very important than the on cycle code destroys the proc, because the proc code doesn’t here.


#Proc code

proc_FO_82A137_code ; $82A15B
	.word <>$829DCB ; loop

This is the most basic proc code, not counting a proc with no code at all. The routine $829DCB is an infinite loop. Once reached, it sets the proc state’s code-reading offset back to the start of the pointer to $829DCB, so that it will run it again on the next cycle. Without any proc code, the proc will only exist for a cycle.


That’s it for now. Whenever I get to it, I’ll post a list of proc-related routines.

5 Likes

It’s time for a WIP list of important (known/understood) routines involved:

System functions:
prlResetProcEngine   | $829BDB | Called on reset, clears RAM for procs
prlCreateProc        | $829BF1 | Creates a proc given type pointer in $6D
prlCreateProcByIndex | $829C99 | Creates a proc in slot given in A, pointer in $6D
prlDeleteProcByIndex | $829CB8 | Deletes a proc in the slot given in A
prlFindProc          | $829CEC | Find proc in $6D, returns index in X with carry set
prlFreeProc          | $829D11 | Given index in X, frees slot

Proc code functions:
prsProcCodeEnd                 | $829DB7 | Frees slot of running proc
prsProcCodeHaltSleep           | $829DC5 | prsProcCodeHalt but also sets the sleep timer to 1
prsProcCodeHalt                | $829DCB | Halts code execution, keeping the code pointer to the start of this code
prsProcCodeCallRoutine         | $829DD3 | Calls routine at long pointer after code word
prsProcCodeCallRoutineWithArgs | $829DEE | As above but is followed by a byte number of bytes to jump over
prsProcCodeSetOnCycle          | $829E19 | Sets OnCycle to word after proc code
prsProcCodeJumpIfBitUnset      | $829E41 | These two check the header bitfield for bits given
prsProcCodeJumpIfBitSet        | $829E4E | by word after proc code, jumping to word after that
prsProcCodeJumpIfRoutineTrue   | $829E77 | These two call routine after proc code and jump
prsProcCodeJumpIfRoutineFalse  | $829E9B | to word after that pointer if routine returns carry set/clear
prsProcCodeCreateProc          | $829EBF | Creates a proc from pointer after proc code
prsProcCodeDeleteProc          | $829ED5 | Finds proc given by pointer after proc code, deletes it
prsProcCodeHaltWhile           | $829F0D | Halts execution if proc given by pointer after proc code is found

A few additional notes:
In an attempt to get better at naming things, these routines have some additional info at the start of their names.

p - This is a label (Use it as a pointer)
r - This is a routine, rather than data or whatever
  - s - This routine returns with rts and needs to be called from within the same bank using jsr
  - l - This routine returns with rtl and can be called from anywhere using jsl
5 Likes