Skip to content

InkCPP Commands

Julian Benda edited this page Jun 26, 2023 · 9 revisions

Commands (or instructions) tell the inkcpp interpreter what to do. Many of the commands in inkcpp are analogous to commands in the Ink JSON format, but there are a few special additions to maintain ink functionality in the binary format. All the currently supported commands are enumerated in command.h.

Commands are encoded in binary. The size of each command varies depending on its arguments, but each begins with two bytes:

  • The Command Type: An unsigned char which indicates which command this is. Again, see command.h.
  • The Command Flag: An unsigned char which may contain multiple flags modifying the behaviour of the command. Most commands don't have flags, and this is just zero. See CommandFlag in command.h.

In this document,

  • uint means a 4-byte unsigned integer. int means a 4-byte signed integer
  • float means an IEEE-754 encoded 4-byte floating point number
  • byte means a signed 1-byte number. ubyte means an unsigned 1-byte number.

WARNING: As mentioned in the Compiler documentation, there is currently no handling for endianness in the compiled data. You have to compile and run InkCPP on machines of matching endianness.

Table of Contents

Value Commands

Value commands out data. If the runner is in evaluation mode (see START_EVAL), then the value is pushed onto the evaluation stack. Otherwise, it's pushed into the output stream.

STR

STR uint offset

Pushes the string at the given offset in the file's string table (0 being the start of the string table).

INT

INT int value

Pushes the given integer value.

BOOL

BOOL int value

pushes a given boolean value (0 for false, 1 for true).

FLOAT

FLOAT float value

Pushes the given floating point number.

LIST

LIST uint offset

Pushes list at given offset in the file's list_table (0 being the start of the list_table).

LIST_FLAG

LIST_FLAG list_flag value

Pushes the given list flag value.

DIVERT_VAL

DIVERT_VAL uint offset

Pushes an offset in the instruction data which could be used later to divert to that instruction. This command only works if the runner is in evaluation mode.

NEWLINE

NEWLINE

Pushes a newline marker.

GLUE

GLUE

Pushes the glue marker.

VOID

VOID

Pushes the void marker. Only works in evaluation mode. Used only when a function is returning nothing.

Implementation Node: Right now I just push a zero since the caller never checks the return value of a void function.

Divert Commands

These commands change the current position of the instruction pointer.

DIVERT

DIVERT uint destination

Immediately changes the instruction pointer to the given destination offset in the instruction data.

If the destination is the byte immediately following the last command in the instruction data, this DIVERT acts the same as a DONE command.

Flags

  • DIVERT_HAS_CONDITION: This divert is conditional. Only execute if the top of the evaluation stack is true. Otherwise, ignore.
  • DIVERT_IS_FALLTHROUGH: This is a special "fallthrough divert" inserted by the compiler to simulate falling out of a container back into its parent. See Fallthrough Diverts on the Containers page.

DIVERT_TO_VARIABLE

DIVERT_TO_VARIABLE uint variableHash

Read the value of the variable with the given hash and jump to that offset in the instruction data.

Flags

  • DIVERT_HAS_CONDITION: This divert is conditional. Only execute if the top of the evaluation stack is true. Otherwise, ignore.

TUNNEL

TUNNEL uint destination

Diverts to the given destination similarly to DIVERT, except push an execution frame onto the stack to remember where we tunneled from. See TUNNEL_RETURN.

TUNNEL_RETURN

TUNNEL_RETURN

Jumps back to where the tunnel was originally started from.

FUNCTION/FUNCTION_RETURN

Same as TUNNEL and TUNNEL_RETURN except output is handled a bit differently.

Flags

  • FUNCTION_TO_VARIABLE: The function address is stored in a variable with the given hash
  • FALLBACK_FUNCTION: The function is a fallback for an external function

TODO: Document this? I think it wasn't even documented in the Ink runtime but I had to reverse engineer what was going on.

DONE

DONE

Pops the active thread or safely ends the story flow without losing the instruction pointer (example: giving control back to the player after creating all the choices).

END

END

Clears the current instruction pointer, signalling the end of the Ink file.

Variable Definitions

These commands deal with variables.

DEFINE_TEMP

DEFINE_TEMP uint variableNameHash

Defines a new temporary variable on the stack with the given hash. Its initial value is popped off the top of the stack.

SET_VARIABLE

SET_VARIABLE uint variableNameHash

Pops the top value off the stack and assigns it to the variable of the given hash. Whether the variable is local or global is determined automatically by checking the stack.

Flags

  • ASSIGNMENT_IS_REDEFINE: Is this assignment a redefinition? I'm not sure why this is important yet but maybe it pertains to a feature I haven't implemented yet? Right now I just ignore it.

Evaluation Stack

These commands push, pop, or otherwise deal with the evaluation stack.

START_EVAL

START_EVAL

Puts the runtime in evaluation mode. Pushed values will now go to the evaluation stack.

END_EVAL

END_EVAL

Takes the runtime out of evaluation mode. Pushed values now go to the output stream.

OUTPUT

OUTPUT

Pops the top value off the evaluation stack and pushes it into the output stream.

POP

POP

Deletes the top element off the evaluation stack.

DUPLICATE

DUPLICATE

Duplicates the top element on the evaluation stack and pushes it onto the top.

PUSH_VARIABLE_VALUE

PUSH_VARIABLE_VALUE uint variableNameHash

Pushes the value of a given variable (local or global) onto the top of the evaluation stack.

PUSH_VARIABLE_POINTER_VALUE

PUSH_VARIABLE_POINTER_VALUE uint variableNameHash

Pushes the value of the variable which is pointer to on the evaluation stack. The ci value is the flag - 1.

VISIT

VISIT

Pushes the visit count of the current container to the top of the evaluation stack.

READ_COUNT

READ_COUNT uint containerIndex

Pushes the visit count of the container with the given index to the top of the evaluation stack.

SEQUENCE

SEQUENCE

Pops the top two values off the evaluation stack. The first is a length, the second is an index.

A random number from 0 to length-1 is pushed onto the stack.

WARNING: Not fully implemented. I think there's some fancy logic in Ink to prevent the same value being picked more than once before the entire list is gone through, but I haven't done that yet. Also I don't know what index is for.

SEED

SEED

Pops the top value off the evaluation stack and uses that as the new seed for random number generation.

Also then pushes void onto the stack (for some reason? I think because it looks like a function call in Ink.)

String Stack

The following instructions deal with the string stack, a special stack for appending strings and numbers into bigger strings.

START_STR

START_STR

Puts the runner into "string mode". Any output or value commands will start appending to a new dynamically created string instead of to the game's output.

END_STR

END_STR

End "string mode". A new value with all the outputted values since START_STR is pushed onto the top of the evaluation stack.

START_TAG

START_TAG

Puts the runner into "tag mode". Any output or value commands will start appending to a new dynamically created string instead of to the game's output.

END_TAG

END_TAG

Ends the "tag mode". A new value with all outputted values since START_TAG is stored in the tag list.

Choice Commands

CHOICE

CHOICE uint destinationInstruction

Creates a new choice that leads to the given offset in the instruction data.

Flags

  • CHOICE_IS_ONCE_ONLY: If set, this choice can only be taken once. This is implemented by checking if the destination container has a visit count of 1 or more.
  • CHOICE_HAS_CONDITION: If set, only show this choice if the top value of the evaluation stack is true
  • CHOICE_HAS_START_CONTENT: If set, pop the top value of the evaluation stack and add it to the choice text
  • CHOICE_HAS_CHOICE_ONLY_CONTENT: If set, pop the top value of the evaluation stack and add it to the choice text
  • CHOICE_IS_INVISIBLE_DEFAULT: Does something I haven't implemented yet. TODO

Binary Operators

All the following commands are binary operators and function in the exact same way. They pop the first two values off the top of the evaluation stack, run their operator on those two values, and push the result to the top of the evaluation stack.

ADD
SUBTRACT
DIVIDE
MULTIPLY
MOD
IS_EQUAL
GREATER_THAN
LESS_THAN
GREATER_THAN_EQUALS
LESS_THAN_EQUALS
NOT_EQUAL
AND
OR
MIN
MAX
?
!?
L^

Unary Operators

Like binary operators, except they only pop one value off the evaluation stack then push the result back.

NOT
NEGATE
LIST_COUNT
LIST_MIN
LIST_MAX
READ_COUNT_VAR reads cont of container pointet to by var on stack
lrnd
LIST_ALL
LIST_INVERT

Container Tracking

These commands are unique to InkCPP and help track the current container (which is needed only for visit counting purposes).

START_CONTAINER_MARKER

START_CONTAINER_MARKER uint containerId

Signals that we are entering the container identified by the given container id.

Flags

  • CONTAINER_MARKER_TRACK_VISITS: If set, track the number of times we have visited this container.

END_CONTAINER_MARKER

END_CONTAINER_MARKER uint containerId

Signals that we are leaving the container identified by the given container id.

This will cause a runtime assertion if the top of the container stack does not match the given id.

Special Behavior: If the result of this command is popping off the last container in the container stack, interpret this as...

  • A void return statement if we are in a function or tunnel
  • An END command otherwise.

Other Commands

CALL_EXTERNAL

CALL_EXTERNAL uint functionNameHash

Special use of the flags byte: The flags byte for this command is interpreted as the number of arguments to pass to the function.

Executes a function bound to the runner using the bind method. The runtime will pop N values off the execution stack (one per argument) and pass them in order of popping to the bound function.

TODO: Link to function binding wiki page.