Skip to content

Assembly

Star Rod assembles instructions for the NEC VR4300 CPU used by the Nintendo 64. The MIPS reference lists the native registers and instructions. This page covers the supplementary syntax and pseudo-instructions provided by Star Rod.

Labels, pointers such as $Script_Example, script variables such as *StoryProgress, and function expressions such as ~Func:get_variable may be used where the corresponding instruction accepts them.

Pseudo-instructions make common sequences easier to read and keep relocatable addresses visible to Star Rod.

InstructionDescription
CLEAR XSet X to zero; equivalent to DADDU X, R0, R0.
COPY X, YCopy Y into X; equivalent to DADDU X, Y, R0.
SUBI X, Y, valueSubtract a signed immediate.
SUBIU X, Y, valueSubtract an unsigned immediate.
LA X, valueLoad an address or word using the ADD variant. LIA is accepted as an alias.
LI X, valueLoad a word using the OR variant. LIO is accepted as an alias.
LIF FX, valueLoad a constant float into a COP1 register.
InstructionDescription
LAB X, ADDRLoad a signed byte.
LABU X, ADDRLoad an unsigned byte.
SAB X, ADDRStore a byte.
LAH X, ADDRLoad a signed half-word.
LAHU X, ADDRLoad an unsigned half-word.
SAH X, ADDRStore a half-word.
LAW X, ADDRLoad a word.
SAW X, ADDRStore a word.
LAF FX, ADDRLoad a float into a COP1 register.
SAF FX, ADDRStore a float from a COP1 register.
LAD FX, ADDRLoad a double into a COP1 register.
InstructionDescription
LTB X, Y (ADDR)Load the Yth signed byte.
LTBU X, Y (ADDR)Load the Yth unsigned byte.
LTH X, Y (ADDR)Load the Yth signed half-word.
LTHU X, Y (ADDR)Load the Yth unsigned half-word.
LTW X, Y (ADDR)Load the Yth word.
LTF FX, Y (ADDR)Load the Yth float into a COP1 register.
STB X, Y (ADDR)Store X at the Yth byte.
STH X, Y (ADDR)Store X at the Yth half-word.
STW X, Y (ADDR)Store X at the Yth word.
STF FX, Y (ADDR)Store FX at the Yth float.
InstructionDescription
PUSH X, Y, ...Reserve the default 0x10-byte local area, save the registers beginning at SP[10], and align the frame to eight bytes.
PUSH[N] X, Y, ...Reserve N bytes instead of the default local area, then save the registers. N must be a multiple of four.
POP X, Y, ...Restore the registers and release the frame. Use the same list and order as PUSH.
JPOP X, Y, ...Restore the registers and return through RA, placing the stack adjustment in the return delay slot.

Repeat the explicit size on POP[N] or JPOP[N]. Stack sizes are hexadecimal by default; append a backtick to write one in decimal.

InstructionDescription
BLT X, Y, labelBranch when X < Y.
BGT X, Y, labelBranch when X > Y.
BLE X, Y, labelBranch when X <= Y.
BGE X, Y, labelBranch when X >= Y.
BLTL, BGTL, BLEL, BGELBranch-likely forms with the same three arguments.

RESERVED may be placed in the delay slot following a pseudo-instruction and a jump. It forces the final instruction emitted by the pseudo-instruction to occupy that delay slot.

LOOP, ENDLOOP, and BREAKLOOP generate labels and branches around a loop body.

LOOP index = start,step,end
...
ENDLOOP

Omitting step uses one. index must be a CPU register. The other values may be CPU registers or immediate integers whose absolute value is at most 7FFF. The condition is checked before the first iteration.

The generated comparison is always index < end; counting loops therefore support ascending loops with a positive step. Write the branches explicitly for a decrementing loop.

InstructionIterations
LOOP A0 = 0,5A0 = 0, 1, 2, 3, 4.
LOOP T1 = 1,2,10T1 = 1, 3, 5, 7, 9.
LOOP SP = SP,4,T4Increment SP by four while SP < T4.
LOOP index < value
...
ENDLOOP

The condition is checked before each iteration. index must be a CPU register; value may be a CPU register or an immediate whose absolute value is at most 7FFF. The supported comparisons are ==, !=, >, >=, <, and <=.

BREAKLOOP exits the innermost active loop.

#DEF assigns a descriptive name beginning with * to a CPU or COP1 register:

#DEF A0, *Counter
CLEAR *Counter
ADDIU *Counter, *Counter, 1
#UNDEF A0
COPY A1, A0

While the definition is active, the bare register name may not be used. #UNDEF may name several registers, such as #UNDEF A0, A1, A2. #UNDEF ALL clears every active register name.

The source-level signature is:

ApiStatus Function(Evt* script, s32 isInitialCall)
RegisterValue
A0Pointer to the caller’s Evt.
A1Nonzero on the first call and zero on subsequent calls.

script->ptrReadPos is at offset 0x0C; it points to the first argument supplied to Call. The four-word functionTemp scratch array begins at offset 0x70 and may retain state between repeated calls. varTable[16] begins at offset 0x84.

ValueNameBehavior
0ApiStatus_BLOCKCall the function again on the next frame.
1ApiStatus_DONE1Finish the call unconditionally.
2ApiStatus_DONE2Finish subject to the interpreter’s disabled-script handling; usual completion result.
3ApiStatus_REPEATCall the function again immediately; mainly used internally by the interpreter.
FFApiStatus_FINISHFinish execution of the script.