slvm/opcodes.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
pub type OpCode = u8;
pub const NOP: OpCode = 0x00;
pub const HALT: OpCode = 0x01;
pub const RET: OpCode = 0x02;
pub const SRET: OpCode = 0x03; // SRET A - R(0) = R(A) and then RET
pub const WIDE: OpCode = 0x04;
const STACK_BASE: OpCode = 0x04;
pub const MOV: OpCode = STACK_BASE + 1; // MOV A B - R(A) = R(B) does not respect closed over values
pub const SET: OpCode = STACK_BASE + 2; // SET A B - R(A) = R(B) respecting local closed over values
pub const CONST: OpCode = STACK_BASE + 3; // CONST A B - R(A) = K(B)
pub const DEF: OpCode = STACK_BASE + 4; // DEF A B - G(B) = R(A)
pub const DEFV: OpCode = STACK_BASE + 5; // DEFV A B - G(B) = R(A) if G(B) is undefined
pub const REFI: OpCode = STACK_BASE + 6; // REFI A B - R(A) = G[B]
pub const CLRREG: OpCode = STACK_BASE + 7; // CLRREG A - R(A) = UNDEFINED (ignores a closed over value)
pub const REGT: OpCode = STACK_BASE + 8; // REGT A - R(A) = TRUE
pub const REGF: OpCode = STACK_BASE + 9; // REGF A - R(A) = FALSE
pub const REGN: OpCode = STACK_BASE + 10; // REGN A - R(A) = NIL
pub const REGC: OpCode = STACK_BASE + 11; // REGC A - R(A) = UNDEFINED
pub const REGB: OpCode = STACK_BASE + 12; // REGB A B - R(A) = Byte(B)
pub const REGI: OpCode = STACK_BASE + 13; // REGI A B - R(A) = Int(B)
pub const CLOSE: OpCode = STACK_BASE + 15; // CLOSE A B - R(A) = closure derived from lambda in R(B)
pub const BMOV: OpCode = STACK_BASE + 16; // BMOV A B C - R(A)..R(A+C) = R(B)..R(B+C) does not respect closed over values
pub const LDSC: OpCode = STACK_BASE + 17; // LDSC A B C - R(A)..R(A+B) = destructured list or vec in R(C) (ignore leftover values)
pub const LDSCR: OpCode = STACK_BASE + 18; // LDSCR A B C - R(A)..R(A+B) = destructured list or vec in R(C) (R(A+B) gets all leftover values)
pub const MDSC: OpCode = STACK_BASE + 19; // MDSC A B C - R(A)..R(A+B) = destructured map in R(C) (ignore leftover values), R(A..) start with keys
pub const COPY: OpCode = STACK_BASE + 20; // COPY A B - R(A) = deep copy of R(B)
pub const FRZ: OpCode = STACK_BASE + 21; // FRZ A - R(A) if a heap object will be made read only
pub const MOVI: OpCode = STACK_BASE + 22; // MOVI A B - R(R(A)) = R(B), A is an indirect index; does not respect closed over values for A
pub const MOVII: OpCode = STACK_BASE + 23; // MOVII A B - R(A) = R(R(B)), B is an indirect index; does not respect closed over values for A
pub const GET: OpCode = STACK_BASE + 24; // GET A B C - if R(A) = R(B) (if it is a complex data structure) element R(C)
pub const SETCOL: OpCode = STACK_BASE + 25; // SETCOL A B C - Set R(B) (if it is a complex data structure) element R(C) to R(A)
// Flow control
const FLOW_BASE: OpCode = STACK_BASE + 26;
// CALL A B C - Call fn R(A) with B args with R(C) as first reg/param
pub const CALL: OpCode = FLOW_BASE;
// TCALL A B - Tail Call fn R(A) with B args with existing stack/regs
pub const TCALL: OpCode = FLOW_BASE + 1;
// CALLG A B C - Call fn G[A] with B args with R(C) as first reg/param
pub const CALLG: OpCode = FLOW_BASE + 2;
// TCALLG A B - Tail Call fn G[A] with B args with existing stack/regs
pub const TCALLG: OpCode = FLOW_BASE + 3;
// CALLM A B - Call current fn with B args with R(C) as first reg/param
pub const CALLM: OpCode = FLOW_BASE + 4;
// TCALLM B - Tail Call current fn with B args with existing stack/regs
pub const TCALLM: OpCode = FLOW_BASE + 5;
// Jumps, all jumps use a signed 24 bit OFFSET (high bit is sign and next 23 are integer).
// This means all jumps are forward or back (negative target).
// JMP OFFSET - Jump to IP + OFFSET
pub const JMP: OpCode = FLOW_BASE + 6;
// JMPT A OFFSET - Jump to current IP + OFFSET if R(A) is truthy (not (nil or false))
pub const JMPT: OpCode = FLOW_BASE + 7;
// JMPF A OFFSET - Jump to current IP + OFFSET if R(A) is falsy (nil or false)
pub const JMPF: OpCode = FLOW_BASE + 8;
// JMPEQ A B OFFSET - compare A and B and jump to IP + OFFSET if they are equal
pub const JMPEQ: OpCode = FLOW_BASE + 9;
// JMPLT A B OFFSET - compare A and B and jump to IP + OFFSET if R(A) < R(B)
pub const JMPLT: OpCode = FLOW_BASE + 10;
// JMPGT A B OFFSET - compare A and B and jump to IP + OFFSET if R(A) > R(B)
pub const JMPGT: OpCode = FLOW_BASE + 11;
// JMPU A OFFSET - Jump to current IP + OFFSET if R(A) is undefined
pub const JMPU: OpCode = FLOW_BASE + 12;
// JMPNU A OFFSET - Jump to current IP + OFFSET if R(A) is NOT undefined
pub const JMPNU: OpCode = FLOW_BASE + 13;
// EQ A B C - R[A] is #t if objects in R[B] - R[C] (inclusive) are the same objects
pub const EQ: OpCode = FLOW_BASE + 14;
// EQUAL A B C - R[A] is #t if objects in R[B] - R[C] (inclusive) are the same objects, values or
// containers with equal values (must be the same container type)
pub const EQUAL: OpCode = FLOW_BASE + 15;
// NOT A B - R[A] is #t if R[B] is falsey and #f otherwise
pub const NOT: OpCode = FLOW_BASE + 16;
// ERR A B - raise error with key R(A) (must be keyword) and value R(B)
pub const ERR: OpCode = FLOW_BASE + 17;
// CCC A B - call with continuation, R(A) must be a lambda that takes one arg (the continuation)
// R(B) is the first reg for the call
pub const CCC: OpCode = FLOW_BASE + 18;
// DFR A - Add a lambda, R(A) to the deferred list.
pub const DFR: OpCode = FLOW_BASE + 19;
// DFRPOP - Pop and call the last deferred lambda.
pub const DFRPOP: OpCode = FLOW_BASE + 20;
// ONERR A - Make R(A) the current error handler and put the previous error handler in R(A).
// If R(A) is nil then remove error handler.
pub const ONERR: OpCode = FLOW_BASE + 21;
// JMPRU A B OFFSET - Jump to current IP + OFFSET if any in R(A)..R(A+B) is undefined
pub const JMPRU: OpCode = FLOW_BASE + 22;
// JMPRNU A B OFFSET - Jump to current IP + OFFSET if any in R(A)..R(A+B) is NOT undefined
pub const JMPRNU: OpCode = FLOW_BASE + 23;
// MKERR A B C - R(A) = error with key R(B) (must be keyword) and value R(C)
pub const MKERR: OpCode = FLOW_BASE + 24;
// ISERR A B - R(A) is #t if R(B) is an error type, #f otherwise
pub const ISERR: OpCode = FLOW_BASE + 25;
// ISOK A B - R(A) is #f if R(B) is an error type, #t otherwise
pub const ISOK: OpCode = FLOW_BASE + 26;
// Basic math
const MATH_BASE: OpCode = FLOW_BASE + 27;
// ADD A B - set R(A) = R(A) + R(B)
pub const ADD: OpCode = MATH_BASE;
// SUB A B - set R(A) = R(A) - R(B)
pub const SUB: OpCode = MATH_BASE + 1;
// MUL A B - set R(A) = R(A) * R(B)
pub const MUL: OpCode = MATH_BASE + 2;
// DIV A B - set R(A) = R(A) / R(B)
pub const DIV: OpCode = MATH_BASE + 3;
// INC A B - Increment the integer in R(A) by B
pub const INC: OpCode = MATH_BASE + 4;
// DEC A B - Decrement the integer in R(A) by B
pub const DEC: OpCode = MATH_BASE + 5;
// NUMEQ A B C - compare (=) in register B (inclusive) to C (inclusive) and set R[A] to the
// result.
pub const NUMEQ: OpCode = MATH_BASE + 6;
// NUMLT A B C - compare (<) in register B (inclusive) to C (inclusive) and set R[A] to the
// result.
pub const NUMLT: OpCode = MATH_BASE + 7;
// NUMGT A B C - compare (>) in register B (inclusive) to C (inclusive) and set R[A] to the
// result.
pub const NUMGT: OpCode = MATH_BASE + 8;
// NUMLTE A B C - compare (<=) in register B (inclusive) to C (inclusive) and set R[A] to the
// result.
pub const NUMLTE: OpCode = MATH_BASE + 9;
// NUMGTE A B C - compare (>=) in register B (inclusive) to C (inclusive) and set R[A] to the
// result.
pub const NUMGTE: OpCode = MATH_BASE + 10;
// Cons cells
const CONS_BASE: OpCode = MATH_BASE + 11;
pub const CONS: OpCode = CONS_BASE; // CONS A B C - R(A) = conscell(R(B), R(C))
pub const CAR: OpCode = CONS_BASE + 1; // CAR A B - R(A) = car(R(B))
pub const CDR: OpCode = CONS_BASE + 2; // CDR A B - R(A) = cdr(R(B))
pub const XAR: OpCode = CONS_BASE + 3; // XAR A B - car(R(A)) = R(B)
pub const XDR: OpCode = CONS_BASE + 4; // XDR A B - cdr(R(A)) = R(B)
pub const LIST: OpCode = CONS_BASE + 5; // LIST A B C - R(A) = list(elements R(B)..R(C)) (R(B) and R(C) are inclusive)
pub const APND: OpCode = CONS_BASE + 6; // APND A B C - R(A) = append lists R(B)..R(C) (R(B) and R(C) are inclusive)
// Vectors
const VEC_BASE: OpCode = CONS_BASE + 7;
// VECMK A B - make a vector with R(B) elements and put it in R(A)
pub const VECMK: OpCode = VEC_BASE;
// VECELS A B - make the length of vec in R(A) R(B)
pub const VECELS: OpCode = VEC_BASE + 1;
// VECPSH A B - push R(B) into vec in R(A)
pub const VECPSH: OpCode = VEC_BASE + 2;
// VECPOP A B - pop from vec in R(A) to R(B)
pub const VECPOP: OpCode = VEC_BASE + 3;
pub const VECMKD: OpCode = VEC_BASE + 4;
// VEC A B C - R(A) = vec(elements R(B)..R(C)) (R(B) inclusive, R(C) exclusive)
pub const VEC: OpCode = VEC_BASE + 5;
// LEN A B - R(A) = length of data in R(B)
pub const LEN: OpCode = VEC_BASE + 6;
// CLR A - Clear the collection in R(A)
pub const CLR: OpCode = VEC_BASE + 7;
// MAPMK A B C - R(A) = map(elements R(B)..R(C)) (R(B) inclusive, R(C) exclusive), alternating key, val pairs
pub const MAPMK: OpCode = VEC_BASE + 8;
// Strings
const STRING_BASE: OpCode = VEC_BASE + 9;
// STR A B C - R(A) = string concatenated from objects in R(A) - R(B) (inclusive)
pub const STR: OpCode = STRING_BASE;
// Types
const TYPE_BASE: OpCode = STRING_BASE + 1;
// TYPE A B - R(A) = type(R(B)) as a StringConst
pub const TYPE: OpCode = TYPE_BASE;
pub const MAX_OP_CODE: OpCode = TYPE_BASE;