FMF:The HVM, From the Top Down

From OHRRPGCE-Wiki
Jump to navigation Jump to search
Mobile-phone.png
This article is about the OHRRPGCE FMF project, which is an alternate implementation of the OHRRPGCE for Java mobile phones. Technical implementation details discussed here should not be confused with those of the RPG format


This page describes the workings of the HVM piece by piece. For your reference, here is a copy of the data structures diagram:


Fmf hvm data structures.png


Here's the lowdown on each data structure and its elements:


Global Variable Table Contains global variables indexed by ID.
ID ID of the variable (implicit).
Value Value of the variable (signed integer).


Running Scripts Table Contains a script UDT and related data for any script which is held in the HVM (running or waiting).
Script The script UDT for a running script.
Wait True if the script is not ready to continue execution; false otherwise.
Wait On A series of bitflags signifying what this script is waiting on, concatenated to the number of cycles left to wait, for wait_cycles. The flags are:

   wait_cycles
    wait_hero
    wait_npcs
    wait_anykey
    wait_pan
    wait_textbox
Note that, in some cases, these flags can all be zero but the script may still be waiting on something.


Script UDT Wraps several fields necessary for a running script.
Local Stack (ref) With the exception of the "push/pop local" commands, all stack operations will take place on the local stack, allowing for easy multitasking. Conceptually "on top" of the local variable array. Stored as a reference, to allow for the possibility of stack sharing.
Program Counter (PC) Stack Each entry represents the current index into the "Bytecode" array (see: "Script Cache Pointer Stack") of the bytecode being executed. At the end of every cycle, the top PC entry is incremented by 1. Of course, any PC above index zero must belong to a subroutine, not a user script.
Script Cache Pointer Stack A series of references to this script's cached data in the "Script Cache" table. If this is null (or -1), this script UDT's cached data has somehow been lost, and must be re-loaded.
Do/If Stack Stack A series of stacks of do_UDT entries. One is pushed each time a dostart or ifstart imperative is encountered, and popped when the corresponding doend or ifend is found. The conglomerate stack is pushed whenever a new subroutine is called.
Local Variable Hash Stack Named variables such as "x" or "test" are stored in the top-most hash table on this stack conglomerate. Although stack-minded gurus detest local stores, they are allowed into Henceforth for the purpose of clarity when debugging a script at run-time.
ID Index of this script in the "Script Lookup Table".
Parent Script Pointer A reference to the script which called this script. When this script terminates, it will "wake" the parent script.
Local Variable Array Stores the parameters and the return value for a given script. Push and pop operations on these affect the stack of the calling script. Indexing this array at -1 will push an effective "return value", to use standard terminology. Could theoretically be used to clobber the stack (but please don't).
String Table Contains a list of Unicode-encoded strings, ordered by ID, for use by this script.
Local Function Start Indices Each script can define local subroutines, which are usually numbered starting from zero, increasing by one monotonically. For example, "\[2] { dup }" defines a local subroutine with ID 2 that serves to duplicate the top of the stack. The interpreter caches the start indices of these script-local subroutines in a small table for fast lookup.
Return Address Stack When a script calls a local subroutine, it pushes the current PC to this stack, and pops it upon returning.


Do UDT A pair of integers used to manage do loops and if statement.
Start Index The value of the PC when a do or if statement is encountered will be unique within a script.
Else Index Contains the index of "else_start" in an if block. (See below). When an entry is pulled from the cache, an end/else index of -1 implies that scanning must occur. -2 indicates that this is a "do" block, not an "if block"; any other number is a valid index in the bytecode array.
End Index Given a do loop's start index, the break command will jump to the next "doend" imperative (taking care to discard any nested "doend"s). Since bytecode is never modified at run-time, this scanning only needs to happen once; the PC index of the "doend" statement can be cached for later. If statements follow the same rule. When an entry is pulled from the cache, an end/else index of -1 implies that scanning must occur; any other number is a valid index in the bytecode array.


Script Lookup Table A full list of all static scripts contains additional data to help retrieve script source from the cache or from the file system.
ID This field is implicit; all scripts from ID 0 to ID N appear in order in this table. If auto-numbered scripts are used, the Henceforth cross-compiler will renumber them so that there are no gaps in this table.
Name The name of this script is stored as a string. The Henceforth cross-compiler will always call a static script by its ID, but developers may choose to call a script by name when debugging.
Chunk Scripts are stored compressed, but compressing a 10-byte file saves you nothing. Hence, there are several scripts "chunked" together in a file. If this value is "5" for example, then the lump "SCRIPTS_5.HF" should be loaded. Please see the relevant documentation for information on how to extract a particular script from its chunk.
Cache A reference to this script's entry in the Script Cache, null or -1 if no entry exists. If several copies of a script exist, they pull only one entry into the cache, and checking this value in the Script Lookup Table is faster than scanning each entry in the Running Scripts Table. Scanning must still occur when an entry is ejected from the Script Cache, but phones with a reasonable amount of memory will rarely have to eject entries.


Script Name Multiplexer A table which holds definitions for named scripts (those defined at runtime).
Name Unlike static scripts, runtime scripts are identified by name alone. Stored in hash table, no two scripts may have the same name (see below).
Definitions A stack of script cache entries corresponding to each definition, stored by reference. If, say, the script "exp" is declared twice, the second entry takes precedence until it is undeclared.


Script Cache A table which contains large amounts of recycleable data, to allow our scripts to run on machines with very little memory. Also contains a few flags.


It is recommended that the script cache stores entries by reference (e.g., in a linked list) instead of by index, since this avoids fragmentation & spurious errors.

New Name Although probably un-necessary, we store the name of the script (or the empty string, for static scripts) to help with debugging.
ID Stores the ID of a static script, or -1 if this is a runtime script.
Pinned A flag: if true, this script cannot be removed from memory without throwing the HVM into an invalid state. An example of this would be a runtime-declared script. Theoretically, a static script that is constantly being reloaded from memory or a script that contains a massive Do Table might be manually pinned by the HVM.
In Use Set to zero initially; incremented by 1 each time a script references this entry. Decremented by 1 each time a script terminates. An entry with an in-use count of 0 is only removed from the Script Cache if more space is needed, since the Do Table is also cached.
Bytecode An array for the script's Format B bytecode. This is static data --after being defined once, it can only be re-loaded --never modified.
Do Table A cache for this script's Do UDTs. When a Script UDT pops its do/it stack, the value is saved here; if the same loop or if statement is encountered again later, a considerable amount of scanning can be skipped.