Wikifang:Network Translation Patchsite/BugVM: Difference between revisions
(Add the $2x opcode lineup.) |
(Fill the $3x line) |
||
Line 29: | Line 29: | ||
|- | |- | ||
! 3x | ! 3x | ||
| || || || || || || || || || || || || || || || | | NOP_30 <br> [[#op_836|$836]] || NOP_31 <br> [[#op_837|$837]] || NOP_32 <br> [[#op_838|$838]] || NOP_33 <br> [[#op_839|$839]] || NOP_34 <br> [[#op_83A|$83A]] || NOP_35 <br> [[#op_87B|$87B]] || NPREF <br> [[#op_87C|$87C]] || JMPT <br> [[#op_887|$887]] || JMP <br> [[#op_8AC|$8AC]] || RET <br> [[#op_8BB|$8BB]] || NOP_3A <br> [[#op_8DB|$8DB]] || NOP_3B <br> [[#op_8DC|$8DC]] || NOP_3C <br> [[#op_8DD|$8DD]] || IMMED <br> [[#op_8DE|$8DE]] || ??? <br> $941 || JAL <br> [[#op_8F0|$8F0]] | ||
|- | |- | ||
! 4x | ! 4x | ||
Line 66: | Line 66: | ||
! Fx | ! Fx | ||
| || || || || || || || || || || || || || || || | | || || || || || || || || || || || || || || || | ||
|} | |} | ||
Line 163: | Line 143: | ||
|- id='op_831' | |- id='op_831' | ||
| POPALL <br> (POP ALL) || $2C || $831 || None || anything (TOP) -> (EMPTY) || Empty the stack. | | POPALL <br> (POP ALL) || $2C || $831 || None || anything (TOP) -> (EMPTY) || Empty the stack. | ||
|- id='op_8DE' | |||
| IMMED <br> (IMMEDiate) || $3D || $8DE || immed (16b) || (TOP) -> immed (TOP) || Push '''immed''' onto the data stack as an immediate value. | |||
|} | |||
=== Execution Control === | |||
{| class='wikitable' | |||
|- | |||
! Opcode !! Encoding(s) !! Native Impl. !! Operand Args !! Stack Args !! Description | |||
|- id='op_887' | |||
| JMPT <br> (JuMP if True) || $37 || $887 || offset (16b) || bool (TOP) -> (TOP) || Jump to a new offset within the current linkage section if '''bool''' is TRUE. | |||
|- id='op_8AC' | |||
| JMP <br> (JuMP) || $38 || $8AC || offset (16b) || None || Jump to a new offset within the current linkage section. | |||
|- id='op_8BB' | |||
| RET <br> (RETurn) || $39 || $8BB || None || None || Return to the previously linked return address. | |||
|- id='op_8F0' | |||
| JAL <br> (Jump And Link) || $3F || $8F0 || offset (16b) || None || Jump to a new offset within the current linkage section. Store the offset of the next instruction on the link stack. | |||
|} | |||
=== NOP and NOP-alikes === | |||
BugVM contains a number of opcodes which do nothing, including both a single implementation of a NOP opcode used to fill in blank spots in the table as well as individual implementations which presumably did something in the past. | |||
{| class='wikitable' | |||
|- | |||
! Opcode !! Encoding(s) !! Native Impl. !! Operand Args !! Stack Args !! Description | |||
|- id='op_5E9' | |||
| NOP <br> (Null OPeration) || Too many to list || $5E9 || None || None || Does nothing. | |||
|- id='op_80B' | |||
| NOP_19 <br> (Null OPeration) || $19 || $80B || None || None || Does nothing. | |||
|- id='op_80C' | |||
| NOP_1A <br> (Null OPeration) || $1A || $80C || None || None || Does nothing. | |||
|- id='op_80D' | |||
| NOP_1B <br> (Null OPeration) || $1B || $80D || None || None || Does nothing. | |||
|- id='op_80E' | |||
| NOP_1C <br> (Null OPeration) || $1C || $80E || None || None || Does nothing. | |||
|- id='op_835' | |||
| NOP_2F <br> (Null OPeration) || $2F || $835 || None || None || Does nothing. | |||
|- id='op_836' | |||
| NOP_30 <br> (Null OPeration) || $30 || $836 || None || None || Does nothing. | |||
|- id='op_837' | |||
| NOP_31 <br> (Null OPeration) || $31 || $837 || None || None || Does nothing. | |||
|- id='op_838' | |||
| NOP_32 <br> (Null OPeration) || $32 || $838 || None || None || Does nothing. | |||
|- id='op_839' | |||
| NOP_33 <br> (Null OPeration) || $33 || $839 || None || None || Does nothing. | |||
|- id='op_83A' | |||
| NOP_34 <br> (Null OPeration) || $34 || $83A || None || None || Does nothing. | |||
|- id='op_87B' | |||
| NOP_35 <br> (Null OPeration) || $35 || $87B || None || None || Does nothing. | |||
|- id='op_87C' | |||
| NPREF <br> (Null PREFix) || $36 || $87C || opcode, ??? || None || Executes the next opcode as normal. | |||
This would appear to be just another NOP, but it's native implementation actually loads and executes the next byte as an opcode. Hence, it works like a prefix byte that does nothing. | |||
Perhaps in a previous revision of the game, NPREF had a use. | |||
|- id='op_8DB' | |||
| NOP_3A <br> (Null OPeration) || $3A || $8DB || None || None || Does nothing. | |||
|- id='op_8DC' | |||
| NOP_3B <br> (Null OPeration) || $3B || $8DC || None || None || Does nothing. | |||
|- id='op_8DB' | |||
| NOP_3C <br> (Null OPeration) || $3C || $8DB || None || None || Does nothing. | |||
|} | |} |
Revision as of 18:35, 24 June 2017
Bugsite contains a virtual machine which executes all (non-IRQ) game logic. It is a stack-based virtual machine with a large number of opcodes specific to the game.
Data and Link Stacks
Two stacks are managed by the VM: a link stack and a data stack. The link stack ($C100) supports up to $3F call frames of 4 bytes each, which is manipulated entirely by the call, jump-far, and return opcodes. The data stack ($C200) supports up to $55 data items of 3 bytes each. Each data item consists of a little-endian 16-bit word followed by a tag byte, which specifies if it's ($3D) an immediate value, ($1D) a word index into the indirect memory array ($C400), or ($1E) a bit index into the predicate array at 3:$D800. Data items are pushed onto the stack as immediate values and then cast to indirect or predicate offsets as necessary. Both stacks grow upwards from their base address.
Linkage Directory
Code and graphical resources exist entirely within sections, which can be referred to by their 16-bit linkage identifier. The linkage identifier indexes a directory starting from $A:$4000, with $800 8-byte directory entries per bank. In practice, while the directory could extend all the way across 8 banks, only 2 are used for the directory. (Effectively, linkages above $1000 are invalid and will horribly crash the game.) Directory entries appear to be stored in order of where their data is stored in ROM.
Each directory index contains a bank index, byte offset into that bank (0 means a pointer of $4000), and total size; with at least graphical resource loads capable of handling sections that span multiple banks. The remaining three bytes are padding and are always zero. There appear to be about 36 unused directory entries from linkage $FDC all pointing to $7F:$70FF with a size of zero.
Initial State
Execution of BugVM always starts from the beginning of linkage $0. Indirect and predicate memory is set to $0 upon game initialization.
Instruction Set
BugVM takes instructions as 8-bit opcodes which can optionally accept additional parameters. Most opcodes take arguments from the stack, rather than from the instruction stream. Native implementations for a particular opcode are referenced from the opcode table at $3E00, reproduced below:
x0 | x1 | x2 | x3 | x4 | x5 | x6 | x7 | x8 | x9 | xA | xB | xC | xD | xE | xF | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0x | NOP $5E9 |
NOP $5E9 |
NOP $5E9 |
??? $671 |
NOP $5E9 |
NOP $5E9 |
STR $68C |
SUML $6C0 |
ANDL $6CA |
OR $6D7 |
XOR $6E2 |
AND $6ED |
CMP_EQ $6F8 |
CMP_NEQ $705 |
CMP_LT $712 |
CMP_LEQ $723 |
1x | CMP_GT $752 |
CMP_GEQ $763 |
??? $772 |
SLA $784 |
SUB $796 |
ADD $7A1 |
MOD $7A9 |
DIV $7C3 |
MUL $7ED |
NOP_19 $80B |
NOP_1A $80C |
NOP_1B $80D |
NOP_1C $80E |
INDIR $80F |
PRED $820 |
NOP $5E9 |
2x | NOP $5E9 |
NOP $5E9 |
NOP $5E9 |
NOP $5E9 |
NOP $5E9 |
NOP $5E9 |
NOP $5E9 |
NOP $5E9 |
NOP $5E9 |
NOP $5E9 |
NOP $5E9 |
NOP $5E9 |
POPALL $831 |
NOP $5E9 |
NOP $5E9 |
NOP_2F $835 |
3x | NOP_30 $836 |
NOP_31 $837 |
NOP_32 $838 |
NOP_33 $839 |
NOP_34 $83A |
NOP_35 $87B |
NPREF $87C |
JMPT $887 |
JMP $8AC |
RET $8BB |
NOP_3A $8DB |
NOP_3B $8DC |
NOP_3C $8DD |
IMMED $8DE |
??? $941 |
JAL $8F0 |
4x | ||||||||||||||||
5x | ||||||||||||||||
6x | ||||||||||||||||
7x | ||||||||||||||||
8x | ||||||||||||||||
9x | ||||||||||||||||
Ax | ||||||||||||||||
Bx | ||||||||||||||||
Cx | ||||||||||||||||
Dx | ||||||||||||||||
Ex | ||||||||||||||||
Fx |
Boolean Logic & Comparison Operators
As convention for this table we treat zero as boolean TRUE and one as boolean FALSE. Other non-zero values are treated as FALSE, but boolean opcodes will not return nonstandard values.
Opcode | Encoding(s) | Native Impl. | Operand Args | Stack Args | Description |
---|---|---|---|---|---|
SUML (SUM Logical) |
$07 | $6C0 | None | arg1, arg2 (TOP) -> bool (TOP) | Add arg1 and arg2. Push TRUE if result is zero, FALSE if non-zero. |
ANDL (AND Logical) |
$08 | $6CA | None | arg1, arg2 (TOP) -> bool (TOP) | Bitwise-AND arg1 and arg2. Push TRUE if result is zero, FALSE if non-zero. |
CMP_EQ (CoMParison EQual) |
$0C | $6F8 | None | arg1, arg2 (TOP) -> bool (TOP) | Compare arg1 and arg2. Push TRUE if both arguments are equal, FALSE otherwise. |
CMP_NEQ (CoMParison Not EQual) |
$0D | $705 | None | arg1, arg2 (TOP) -> bool (TOP) | Compare arg1 and arg2. Push TRUE if both arguments are not equal, FALSE otherwise. |
CMP_LT (CoMParison Less Than) |
$0E | $712 | None | arg1, arg2 (TOP) -> bool (TOP) | Compare arg1 and arg2. Push TRUE if arg1 is less than arg2, FALSE otherwise. |
CMP_LEQ (CoMParison Less or EQual) |
$0F | $723 | None | arg1, arg2 (TOP) -> bool (TOP) | Compare arg1 and arg2. Push TRUE if arg1 is less than or equal to arg2, FALSE otherwise. |
CMP_GT (CoMParison Greater Than) |
$10 | $752 | None | arg1, arg2 (TOP) -> bool (TOP) | Compare arg1 and arg2. Push TRUE if arg1 is greater than arg2, FALSE otherwise. |
CMP_GEQ (CoMParison Greater or EQual) |
$11 | $763 | None | arg1, arg2 (TOP) -> bool (TOP) | Compare arg1 and arg2. Push TRUE if arg1 is greater than or equal to arg2, FALSE otherwise. |
Bitwise logic
Opcode | Encoding(s) | Native Impl. | Operand Args | Stack Args | Description |
---|---|---|---|---|---|
OR (bitwise OR) |
$09 | $6D7 | None | arg1, arg2 (TOP) -> value (TOP) | Bitwise-OR arg1 and arg2 as the return value. |
XOR (bitwise eXclusive OR) |
$0A | $6E2 | None | arg1, arg2 (TOP) -> value (TOP) | Bitwise-XOR arg1 and arg2 as the return value. |
AND (bitwise AND) |
$0B | $6ED | None | arg1, arg2 (TOP) -> value (TOP) | Bitwise-AND arg1 and arg2 as the return value. |
SLA (Shift-Left Arithmetic) |
$13 | $784 | None | bits, shift (TOP) -> value (TOP) | Shift bits left, shift times, while inserting zero bits, to produce value. |
Arithmetic
Opcode | Encoding(s) | Native Impl. | Operand Args | Stack Args | Description |
---|---|---|---|---|---|
SUB (SUBtraction) |
$14 | $796 | None | minuend, subtrahend (TOP) -> difference (TOP) | Subtract subtrahend from minuend to produce difference. |
ADD (ADDition) |
$15 | $7A1 | None | addend1, addend2 (TOP) -> sum (TOP) | Add addend1 to addend2 to produce sum. |
MOD (MODulo) |
$16 | $7A9 | None | dividend, divisor (TOP) -> remainder (TOP) | Integer-divide dividend by divisor and return only the remainder. |
DIV (DIVide) |
$17 | $7C3 | None | dividend, divisor (TOP) -> quotient (TOP) | Integer-divide dividend by divisor and return only the quotient. |
MUL (MULtiply) |
$18 | $7ED | None | multiplicand, multiplier (TOP) -> product (TOP) | Multiply multiplicand by multiplier to produce the product. |
Memory & Stack Manipulation
Opcode | Encoding(s) | Native Impl. | Operand Args | Stack Args | Description |
---|---|---|---|---|---|
STR (SToRe) |
$06 | $68C | None | &address, value (TOP) -> (TOP) | Store the value at the memory location referenced by &address. The exact memory operations performed depend on the target of *address.
If address is an indirect index, the memory location referenced by *address will be set to value. If address is a predicate index, the bit referenced by *address will be set or reset based on if value is zero or non-zero. It is illegal to STR into an immediate value, and doing so will cause the VM to halt operation. |
INDIR (INDIRect) |
$1D | $80F | None | immed (TOP) -> &address (TOP) | Cast the value immed into an indirect memory index &address. |
PRED (PREDicate) |
$1E | $820 | None | immed (TOP) -> &pred (TOP) | Cast the value immed into a predicate memory index &pred. |
POPALL (POP ALL) |
$2C | $831 | None | anything (TOP) -> (EMPTY) | Empty the stack. |
IMMED (IMMEDiate) |
$3D | $8DE | immed (16b) | (TOP) -> immed (TOP) | Push immed onto the data stack as an immediate value. |
Execution Control
Opcode | Encoding(s) | Native Impl. | Operand Args | Stack Args | Description |
---|---|---|---|---|---|
JMPT (JuMP if True) |
$37 | $887 | offset (16b) | bool (TOP) -> (TOP) | Jump to a new offset within the current linkage section if bool is TRUE. |
JMP (JuMP) |
$38 | $8AC | offset (16b) | None | Jump to a new offset within the current linkage section. |
RET (RETurn) |
$39 | $8BB | None | None | Return to the previously linked return address. |
JAL (Jump And Link) |
$3F | $8F0 | offset (16b) | None | Jump to a new offset within the current linkage section. Store the offset of the next instruction on the link stack. |
NOP and NOP-alikes
BugVM contains a number of opcodes which do nothing, including both a single implementation of a NOP opcode used to fill in blank spots in the table as well as individual implementations which presumably did something in the past.
Opcode | Encoding(s) | Native Impl. | Operand Args | Stack Args | Description |
---|---|---|---|---|---|
NOP (Null OPeration) |
Too many to list | $5E9 | None | None | Does nothing. |
NOP_19 (Null OPeration) |
$19 | $80B | None | None | Does nothing. |
NOP_1A (Null OPeration) |
$1A | $80C | None | None | Does nothing. |
NOP_1B (Null OPeration) |
$1B | $80D | None | None | Does nothing. |
NOP_1C (Null OPeration) |
$1C | $80E | None | None | Does nothing. |
NOP_2F (Null OPeration) |
$2F | $835 | None | None | Does nothing. |
NOP_30 (Null OPeration) |
$30 | $836 | None | None | Does nothing. |
NOP_31 (Null OPeration) |
$31 | $837 | None | None | Does nothing. |
NOP_32 (Null OPeration) |
$32 | $838 | None | None | Does nothing. |
NOP_33 (Null OPeration) |
$33 | $839 | None | None | Does nothing. |
NOP_34 (Null OPeration) |
$34 | $83A | None | None | Does nothing. |
NOP_35 (Null OPeration) |
$35 | $87B | None | None | Does nothing. |
NPREF (Null PREFix) |
$36 | $87C | opcode, ??? | None | Executes the next opcode as normal.
This would appear to be just another NOP, but it's native implementation actually loads and executes the next byte as an opcode. Hence, it works like a prefix byte that does nothing. Perhaps in a previous revision of the game, NPREF had a use. |
NOP_3A (Null OPeration) |
$3A | $8DB | None | None | Does nothing. |
NOP_3B (Null OPeration) |
$3B | $8DC | None | None | Does nothing. |
NOP_3C (Null OPeration) |
$3C | $8DB | None | None | Does nothing. |