Difference between revisions of "MOTHER 3:Game logic"
|Line 61:||Line 61:|
| <tt>0A XX YY ZZ</tt>
| <tt>0A XX YY ZZ</tt>
| <tt>0B XX YY ZZ</tt>
| <tt>0B XX YY ZZ</tt>
Revision as of 17:56, 19 June 2020
|# of Entries||2002 (0x7D2)|
|Total Length||1907428 bytes (0x1D1AE4)|
|Back to the ROM map|
This large table controls most of the game's scripted logic. It's responsible for displaying text from the main text table, starting battles, setting event flags, on-screen animations, and more.
There are 2002 offsets at the start of this table; the offsets occur in pairs, so there are 1001 logical entries overall. The entries correspond to the main text table. Similar to the main text table, the first offset in each pair points to a 16-bit offset table, and the second offset in each pair points to the actual script data. The 16-bit offset table has a 16-bit header indicating how many offsets are in that table. The offsets only use 14 bits. The 15th one seems to have no purpose and the 16th one is used as a flag of sorts.
A script consists of a list of 4-byte codes. The first of these 4 bytes denotes the code type. It must be between 0 and 0xE, inclusive. The remaining three bytes, ordered as [XX YY ZZ], are usually formatted as either a sign-extended 32-bit value 0xZZYYXX, or as an unsigned 16-bit value 0xZZYY (the XX byte is ignored).
The script system has an internal stack of 4-byte values. This stack is used to pass arguments to, and return-values from, the script codes that get called.
There are also two 16-bit "auxiliary" slots used when calling into subroutines.
The following table summarizes known code values:
|00 XX YY ZZ||Read from stack index (ZZYY + value in auxiliary slot XX), push result. This stack index is relative to the start of the stack, not the current stack pointer.|
|01 XX YY ZZ||Push ZZYYXX to the stack (sign-extended)|
|02 XX YY ZZ||Unknown|
|03 XX YY YY||Copy value from (current stack pointer + XX) to YYYY|
|04 00 YY ZZ||Extended code|
|05 XX YY ZZ||Switch to script container 0 and jump to offset ZZYY. Reads value from auxiliary slot (XX + 1) and copies to current stack pointer. Stores return pointer to (stack pointer+ 1). Stores old stack pointer to auxiliary slot 1. Does not change the stack pointer. XX is always 0 in practice.|
|06 XX YY ZZ||Return to 05 caller. Arguments are XX and ZZYY. Restores old stack pointer from auxiliary slot XX. Reads from stack pointer and stores to auxiliary slot XX. Reads return offset from (stack pointer + 1). Decrements stack pointer by ZZYY.|
|07 XX YY ZZ||Unconditionally jump to offset ZZYY and return; XX is unknown|
|08 XX YY ZZ||Return to 07 caller. Arguments are XX and ZZYY, purpose unknown|
|09 XX YY ZZ||End of script|
|0A XX YY ZZ||Store YYXX to auxiliary slot 1.|
|0B XX YY ZZ||Increment the current stack pointer by ZZYYXX|
|0C 00 YY ZZ||Unconditionally jump to offset ZZYY and don't return|
|0D 00 YY ZZ||If the value at the top of the stack is zero, jump to offset ZZYY and don't return|
|0E XX 00 00||Extended math|
Extended code (04)
The 04 00 YY ZZ code calls one of an extended set of 256 additional control codes. Because there are so many, they are documented in a separate page.
Extended math (0E)
0E XX 00 00 performs a basic math operation. It operates on the top of the stack; it either uses zero, one or two values. In this subsection, $1 refers to the value on the top of the stack and $2 refers to the value underneath $1.
XX may only take values from 0 to 0x13, inclusive.
|0E 00 00 00||Pop $1, push -$1|
|0E 01 00 00||Pop $1 and $2, push $1 + $2|
|0E 02 00 00||Pop $1 and $2, push $2 - $1|
|0E 03 00 00||Pop $1 and $2, push $1 * $2|
|0E 04 00 00||Pop $1 and $2, push $2 / $1|
|0E 05 00 00||Pop $1 and $2, push $2 % $1|
|0E 06 00 00||Pop $1, push $1 + 1|
|0E 07 00 00||Pop $1, push $1 - 1|
|0E 08 00 00||Pop $1 and $2, push $1 & $2|
|0E 09 00 00||Pop $1 and $2, push $1 | $2|
|0E 0A 00 00||Pop $1 and $2. If $1 == $2, push 1; else, push 0|
|0E 0B 00 00||Pop $1 and $2. If $1 != $2, push 1; else, push 0|
|0E 0C 00 00||Pop $1 and $2. If $1 > $2, push 1; else, push 0|
|0E 0D 00 00||Pop $1 and $2. If $1 < $2, push 1; else, push 0|
|0E 0E 00 00||Pop $1 and $2. If $1 >= $2, push 1; else, push 0|
|0E 0F 00 00||Pop $1 and $2. If $1 <= $2, push 1; else, push 0|
|0E 10 00 00||Without popping $1, push a copy of $1|
|0E 11 00 00||Decrement the stack pointer by 1|
|0E 12 00 00||Decrement the stack pointer by 1|
|0E 13 00 00||No operation|
Since the scripting format is quite complex, it's helpful to compile a list of known sequences of commands. They are on a separate page.
Say we wanted to get the scripts for the area with Alec's house. This area is map 21. We need to add 1 to this value because the first entry in this table (and in the main text table, too) correspond to a special null-area with shared text blocks. So, we're looking for entry 22 in this table. Skipping the 4-byte header, and considering that each entry has two offsets, we get to $1198CC4:
1198CC4: 40 AE 03 00 78 AE 03 00
So the 16-bit offset table is located at $11D3A50 (1198C10 + 3AE40), and the script data itself starts at $11D3A88 (1198C10 + 3AE78). The offset table looks like this:
11D3A50: 1B 00 07 02 0A 02 1B 02 84 02 C2 02 D4 02 1F 03 3F 03 54 03 5B 03 68 03 75 03 82 03 8F 03 9C 03 A4 03 AB 04 AE 04 14 05 3C 05 46 05 B4 05 B9 05 BF 05 D0 05 D4 05 DB 05
Each offset is in units of 32 bits; that is, to get the absolute address of an actual script, you need to multiply the offset by 4, and then add it to the script pointer (in this case, add it to $11D3A88).
Now, say we want to see the save frog's script. His object has a script value of 2, but since the game adds 5, we actually want the 7th offset: 0x33F. Multiplying by 4 and adding to the script pointer,
frog script address = $11D3A88 + (0x33F * 4) = $11D4784
This finally brings us to his script:
0B 0D 00 00 = Add 5 to the current stack pointer 0A 0D 00 00 = Affects the stack in a similar way (These two instructions have an unknown motivation; many scripts do not have them, and those that do often function perfectly fine without them)
01 62 01 00 = Push 0x162 to the stack 04 00 0D 00 = Check event flag 0x162; overwrite the current stack value with the result (In this case, the 0x162 on the stack gets overwritten with a 0 or 1, depending on whether or not the flag is set)
01 00 00 00 = Push 0 to the stack 0E 0A 00 00 = Compare the value of the flag with 0, and store the result (true or false) on the stack 0D 00 4F 03 = If it's false (that is, if flag != 0), jump to script number 0x34F (labelled here as already_talked)
01 FF FF FF = Push 0xFFFFFFFF (-1) to the stack 01 FF FF FF = Push -1, again 01 A5 00 00 = Push 0xA5 to the stack 04 00 33 00 = Display string 0xA5 from entry 0 in the main text table, using the default speaker (first -1), the frog itself (The second -1 does absolutely nothing, but it needs to be there)
01 20 00 00 = Push 0x20 to the stack 04 00 00 00 = Delay parsing by 0x20 frames
01 62 01 00 = Push 0x162 to the stack 01 01 00 00 = Push 1 to the stack 04 00 0A 00 = Set flag 0x162 to 1
already_talked: 01 FF FF FF = Push 0xFFFFFFFF (-1) to the stack 01 FF FF FF = Push -1, again 05 00 A7 10 = Call the save frog sequence (not sure about the details on this)
09 00 00 00 = End