This document defines the formal MVP specification for AI-Assembly ISA v0.
The goal of this ISA is not hardware compatibility or production efficiency. The goal is to provide a small, deterministic, fully traceable execution model suitable for:
- reference VM implementation
- synthetic dataset generation
- step-by-step supervision
- algorithmic reasoning experiments
- experiments toward execution-style behavior (results on this ISA may not generalize to other ISAs or task distributions)
This spec is intentionally strict. If an implementation differs from this document, the document wins.
This specification defines the MVP execution profile only.
Included instructions:
CONST
MOV
LOAD
STORE
READ
WRITE
ADD
SUB
CMP
TEST
JMP
JZ
JNZ
JL
JLE
JG
JGE
HALT
Not included in MVP:
MUL DIV MOD INC DEC
AND OR XOR NOT
PUSH POP CALL RET
NOP
These may be added in a later version, but are out of scope for v0-mvp.
The machine has exactly 8 general-purpose registers:
R0 R1 R2 R3 R4 R5 R6 R7
All registers store signed 16-bit integers.
The machine uses:
signed int16
Value range:
-32768 .. 32767
All arithmetic results are reduced modulo 2^16 and then interpreted as signed 16-bit integers.
Examples:
32767 + 1 -> -32768-32768 - 1 -> 32767
The machine has three flags:
Z N C
Definitions:
Z = 1if the result of the relevant operation is exactly0, else0N = 1if the signed result of the relevant operation is negative, else0C = 1if the relevant operation produced unsigned carry or borrow according to the rules below, else0
The machine has one instruction pointer:
IP
IP is the zero-based index of the current instruction in the resolved program.
The machine has flat memory:
M[0..255]
Memory contains 256 signed 16-bit cells.
Input and output are modeled as indexed arrays:
IN[0..K]
OUT[0..K]
IN is provided at execution start.
OUT is initially empty. An unwritten output slot is considered unset.
The machine state includes:
HALTED: 0 | 1
ERROR: NONE | <ERROR_CODE>
If ERROR != NONE, the machine is in a terminal error state.
If HALTED = 1, the machine is in a terminal success state.
No instruction may execute once the machine is terminal.
- One instruction per line
- Labels are allowed
- Labels map to instruction indices after parsing
- Blank lines are allowed
- Comments are not part of the canonical training format
Example:
START:
READ R1, input[0]
READ R2, input[1]
ADD R3, R1, R2
WRITE output[0], R3
HALT
Valid labels:
- must start with
A-Zor_ - remaining chars may be
A-Z,a-z,0-9,_
Examples:
LOOPEND_1_START
Valid registers:
R0 R1 R2 R3 R4 R5 R6 R7
Any other register token is invalid.
For MVP, only direct integer addressing is allowed for LOAD and STORE:
LOAD R1, [10]
STORE [20], R2
Dynamic addressing such as [R1] is not part of the MVP.
Canonical syntax:
READ R1, input[0]
WRITE output[0], R2
Only non-negative integer indices are allowed.
The parser must:
- tokenize each line
- validate opcode and operand count
- validate operand forms
- build a linear instruction list
- collect label definitions
Labels resolve to instruction indices in the instruction list after label-only lines are removed.
Example:
START:
CONST R1, 1
JMP START
Resolved instructions:
0: CONST R1, 1
1: JMP 0
The implementation must reject the program before execution if any of the following occur:
- unknown opcode
- wrong operand count
- invalid register
- invalid integer literal
- invalid label syntax
- duplicate label
- unresolved label
- invalid
input[k]oroutput[k]syntax - invalid
[addr]syntax
At execution start:
- all registers are
0 - all flags are
0 IP = 0- all memory cells are
0unless explicitly initialized by the runtime - all output slots are unset
HALTED = 0ERROR = NONE
Optionally, the runtime may provide initial memory contents. If so, that must be explicit in the task input and trace format.
All stored machine values must always be normalized to signed int16.
Define:
wrap16(x) = ((x mod 65536) interpreted as signed int16)
For carry detection, each operand may also be viewed as unsigned 16-bit:
u16(x) in 0..65535
For ADD dst, a, b:
- compute
raw = u16(a) + u16(b) C = 1ifraw > 65535, else0- stored result is
wrap16(raw)
For SUB dst, a, b and CMP a, b:
- compute
raw = u16(a) - u16(b) C = 1ifu16(a) < u16(b), else0- stored or compared result is
wrap16(raw)
This treats C as unsigned borrow for subtraction-style operations.
Only these instructions update flags in the current profile:
ADD
SUB
CMP
TEST
Rules:
ZandNare derived from the signed int16 resultCfollows the carry/borrow rules above
These instructions do not update flags:
CONST MOV LOAD STORE READ WRITE JMP JZ JNZ JL JLE JG JGE HALT
All semantics below assume execution begins in a non-terminal state.
Operation:
dst := wrap16(imm)
Effects:
- registers updated
- flags unchanged
IP := IP + 1
Errors:
- none at runtime if parse succeeded
Operation:
dst := src
Effects:
- destination register updated
- flags unchanged
IP := IP + 1
Operation:
dst := M[addr]
Effects:
- destination register updated
- flags unchanged
IP := IP + 1
Errors:
OOB_MEMORYifaddr < 0 or addr > 255
Note:
In canonical MVP syntax, invalid addresses should normally be blocked at parse time if literal and out of range. Runtime handling still exists for robustness.
Operation:
M[addr] := src
Effects:
- memory updated
- flags unchanged
IP := IP + 1
Errors:
OOB_MEMORYifaddr < 0 or addr > 255
Operation:
dst := IN[k]
Effects:
- destination register updated
- flags unchanged
IP := IP + 1
Errors:
OOB_INPUTifkis not present in the provided input
Operation:
OUT[k] := src
Effects:
- output updated
- flags unchanged
IP := IP + 1
Errors:
OOB_OUTPUTif the runtime enforces bounded output andkis outside the allowed range
Default MVP runtime policy:
- output is sparse and grows on write
- therefore
WRITEdoes not error for non-negativek
Operation:
raw := u16(a) + u16(b)result := wrap16(raw)dst := result
Flags:
Z := 1 if result == 0 else 0N := 1 if result < 0 else 0C := 1 if raw > 65535 else 0
Effects:
- destination register updated
- flags updated
IP := IP + 1
Operation:
raw := u16(a) - u16(b)result := wrap16(raw)dst := result
Flags:
Z := 1 if result == 0 else 0N := 1 if result < 0 else 0C := 1 if u16(a) < u16(b) else 0
Effects:
- destination register updated
- flags updated
IP := IP + 1
Operation:
- compute subtraction result exactly as in
SUB, but do not store it
Flags:
Z := 1 if result == 0 else 0N := 1 if result < 0 else 0C := 1 if u16(a) < u16(b) else 0
Effects:
- registers unchanged
- memory unchanged
IP := IP + 1
Operation:
- compute
result := a
Flags:
Z := 1 if result == 0 else 0N := 1 if result < 0 else 0C := 0
Effects:
- registers unchanged
- memory unchanged
IP := IP + 1
Operation:
IP := resolved_label_index
Effects:
- only
IPchanges - flags unchanged
Operation:
- if
Z == 1, thenIP := resolved_label_index - else
IP := IP + 1
Effects:
- flags unchanged
Operation:
- if
Z == 0, thenIP := resolved_label_index - else
IP := IP + 1
Effects:
- flags unchanged
Operation:
- if
N == 1, thenIP := resolved_label_index - else
IP := IP + 1
Effects:
- flags unchanged
Operation:
- if
N == 1 or Z == 1, thenIP := resolved_label_index - else
IP := IP + 1
Effects:
- flags unchanged
Operation:
- if
N == 0 and Z == 0, thenIP := resolved_label_index - else
IP := IP + 1
Effects:
- flags unchanged
Operation:
- if
N == 0 or Z == 1, thenIP := resolved_label_index - else
IP := IP + 1
Effects:
- flags unchanged
Operation:
HALTED := 1
Effects:
- machine becomes terminal
- registers, memory, flags, output unchanged
IPremains at theHALTinstruction index in the terminal state
Execution proceeds as follows:
- If
HALTED = 1orERROR != NONE, stop. - If
IP < 0orIP >= program_length, setERROR := IP_OOBand stop. - Fetch instruction at
IP. - Execute according to this spec.
- If step counter exceeds configured maximum, set
ERROR := MAX_STEPS_EXCEEDEDand stop. - Repeat.
The MVP runtime must support these terminal error codes:
NONE
OOB_MEMORY
OOB_INPUT
OOB_OUTPUT
BAD_OPCODE
BAD_OPERAND
IP_OOB
MAX_STEPS_EXCEEDED
For the strict reference implementation, parse-time failures should normally prevent execution and be reported separately. BAD_OPCODE and BAD_OPERAND exist mainly for defensive runtime behavior and corrupt input handling.
The canonical textual state format is:
IP=<int>
R0=<int> R1=<int> R2=<int> R3=<int> R4=<int> R5=<int> R6=<int> R7=<int>
Z=<0|1> N=<0|1> C=<0|1>
OUT[0]=<value_or_underscore>
HALTED=<0|1>
ERROR=<ERROR_CODE>
If memory must be shown, canonical compact format is:
MEM[M[3]=7 M[10]=-1]
Rules:
- touched memory cells only
- increasing address order
- omit
MEM[...]if no cells were written or explicitly initialized
One execution step is serialized as:
STEP <n>
EXEC <instruction_text>
IP=<int>
R0=<int> R1=<int> R2=<int> R3=<int> R4=<int> R5=<int> R6=<int> R7=<int>
Z=<0|1> N=<0|1> C=<0|1>
OUT[0]=<value_or_underscore>
HALTED=<0|1>
ERROR=<ERROR_CODE>
If memory is present, append:
MEM[...]
The EXEC line must use canonical instruction formatting.
The serializer must emit instructions exactly in one of these forms:
CONST Rn, imm
MOV Rn, Rm
LOAD Rn, [addr]
STORE [addr], Rn
READ Rn, input[k]
WRITE output[k], Rn
ADD Rn, Ra, Rb
SUB Rn, Ra, Rb
CMP Ra, Rb
TEST Ra
JMP LABEL
JZ LABEL
JNZ LABEL
JL LABEL
JLE LABEL
JG LABEL
JGE LABEL
HALT
The ordered jumps JL/JLE/JG/JGE use the current Z and N flags only.
This means:
- they operate on the most recent wrapped signed comparison state
- they are deterministic and simple for learning
- they are not x86-style overflow-aware signed branches
Practical rule:
- use
CMPorTESTimmediately before ordered jumps - treat them as ISA-native ordered predicates over the current int16 state, not as hardware-compatible condition codes
Whitespace rules:
- one space after opcode
- comma followed by one space
- no extra spaces
Program:
READ R1, input[0]
READ R2, input[1]
ADD R3, R1, R2
WRITE output[0], R3
HALT
Input:
input[0]=7
input[1]=5
Initial state:
IP=0
R0=0 R1=0 R2=0 R3=0 R4=0 R5=0 R6=0 R7=0
Z=0 N=0 C=0
OUT[0]=_
HALTED=0
ERROR=NONE
After step 1:
STEP 1
EXEC READ R1, input[0]
IP=1
R0=0 R1=7 R2=0 R3=0 R4=0 R5=0 R6=0 R7=0
Z=0 N=0 C=0
OUT[0]=_
HALTED=0
ERROR=NONE
After step 2:
STEP 2
EXEC READ R2, input[1]
IP=2
R0=0 R1=7 R2=5 R3=0 R4=0 R5=0 R6=0 R7=0
Z=0 N=0 C=0
OUT[0]=_
HALTED=0
ERROR=NONE
After step 3:
STEP 3
EXEC ADD R3, R1, R2
IP=3
R0=0 R1=7 R2=5 R3=12 R4=0 R5=0 R6=0 R7=0
Z=0 N=0 C=0
OUT[0]=_
HALTED=0
ERROR=NONE
After step 4:
STEP 4
EXEC WRITE output[0], R3
IP=4
R0=0 R1=7 R2=5 R3=12 R4=0 R5=0 R6=0 R7=0
Z=0 N=0 C=0
OUT[0]=12
HALTED=0
ERROR=NONE
After step 5:
STEP 5
EXEC HALT
IP=4
R0=0 R1=7 R2=5 R3=12 R4=0 R5=0 R6=0 R7=0
Z=0 N=0 C=0
OUT[0]=12
HALTED=1
ERROR=NONE
A conforming reference VM must:
- reject invalid programs deterministically
- execute valid programs exactly according to this spec
- produce canonical terminal states
- produce canonical traces when trace mode is enabled
- enforce
max_steps - preserve determinism across runs
The following design areas are intentionally deferred:
- dynamic memory addressing
- stack semantics
- subroutine semantics
- multiplication and division edge cases
- bitwise flag policy
- structured control-flow sugar
- compressed trace formats
- alternative IR forms
With this specification frozen, the next artifact should be:
python-vm-reference-plan.md
or directly:
reference_vm.py + tests