268 lines
10 KiB
Markdown
268 lines
10 KiB
Markdown
# Bytecode Documentation
|
|
|
|
## Instructions
|
|
Each instruction consists of a mnemonic and some number of arguments. The
|
|
numeric value for each instruction is the same length, but the arguments can be
|
|
different lengths, and each instruction can have a different number of
|
|
arguments.
|
|
|
|
In this document, the layout for data is specified by a number of fields
|
|
separated by commas. When represented in raw bits, these fields are next to each
|
|
other with no padding in between. For example:
|
|
```text
|
|
length:u64, data:[i8]
|
|
```
|
|
represents an unsigned 64 bit integer field called "length" and a field called
|
|
"data" which is an array of signed 8 bit integers. The value right before an
|
|
array is the value which determines its length.
|
|
|
|
The argument types are as follows:
|
|
| Name | Description |
|
|
|:------------------|:--------------------------------------|
|
|
| i8, i16, i32, i64 | 8, 16, 32, or 64 bit signed integer |
|
|
| u8, u16, u32, u64 | 8, 16, 32, or 64 bit unsigned integer |
|
|
| double | Equivilant to the C type "double" |
|
|
| reg | register (format: type:u8,which:u32) |
|
|
| str | string (format: length:u64,data:[i8]) |
|
|
| bool | boolean (format: u8) |
|
|
|
|
### Registers
|
|
Most instructions take register numbers instead of direct arguments. Registers
|
|
take a type and a number. For example "lexenv2" is the third (counting from 0)
|
|
lexenv register or "arg0" is the first argument register.
|
|
|
|
| Mnemonic | ID | Description |
|
|
|:---------|:--:|:--------------------------------------------------------------|
|
|
| val | 0 | General value registers (clobbered by calls) |
|
|
| saved | 1 | Callee saved ragisters (val registers are clobbered by calls) |
|
|
| arg | 2 | Function argument registers (clobbered by calls) |
|
|
| ret | 3 | Function return value registers (clobbered by calls) |
|
|
|
|
### Instruction List
|
|
|
|
- NIL dest:reg
|
|
Load the literal nil into DEST
|
|
|
|
- T def:reg
|
|
Load the literal t into DEST
|
|
|
|
- STRING dest:reg, value:str
|
|
Load the string of LENGTH bytes from DATA into dest.
|
|
|
|
- INT dest:reg, value:i64
|
|
Convert VALUE into an int object and store it into DEST
|
|
|
|
- FLOAT dest:reg, value:double
|
|
Convert VALUE into a float object and store it into DEST
|
|
|
|
- CONS dest:reg, car:reg, cdr:reg
|
|
Create a cons object with CAR and CDR and store it into REG.
|
|
|
|
- LIST dest:reg, count:u64
|
|
Create a list from the first COUNT "arg" registers and store it into DEST.
|
|
|
|
- VECTOR dest:reg, count:u64
|
|
Create a vector from the first COUNT "arg" registers and store it into DEST.
|
|
|
|
- LENGTH dest:reg, thing:reg
|
|
Place the length of the THING, a string, vector, or list, into DEST.
|
|
|
|
- INTERN\_LIT reg:reg, name:str
|
|
INTERN\_DYN reg:reg, name:reg
|
|
These instructions convert the string literal or register containing a string
|
|
NAME into a symbol and store the symbol into REG.
|
|
|
|
- TYPE_OF dest:reg, thing:reg
|
|
Place a symbol representing the type of THING into DEST.
|
|
|
|
- SYMBOL\_NAME dest:reg, sym:reg
|
|
Store the name of SYM into DEST.
|
|
|
|
- MOV dest:reg, src:reg
|
|
Copy the value in the register SRC into the register DEST.
|
|
|
|
- FUNCALL reg:reg, argc:u64
|
|
Call the function in REG. This should either be a function object or a symbol
|
|
which has a value as a function. The return values are placed into the "ret"
|
|
registers. ARGC is the number of "arg" registers that have been set for this
|
|
function call.
|
|
|
|
- RETVAL_COUNT count:u8
|
|
Declare that first COUNT return values have been set (if they have not been
|
|
touched during this lexenv, they will be set to nil). Without this, the
|
|
highest "ret" register written to is the number to use. Without this, assume
|
|
one return value.
|
|
|
|
- GET\_RETVAL\_COUNT dest:reg
|
|
Place the count last set with RETVAL\_COUNT into DEST.
|
|
|
|
- ENTER\_LEXENV count:u64
|
|
Enter a new lexical environment. COUNT is the number of instructions that this
|
|
environment lasts.
|
|
|
|
- ENTER\_BLOCK name:str, count:u64
|
|
LEAVE\_BLOCK name:str
|
|
Enter a new named block which is identified by the symbol named NAME. The
|
|
block is COUNT instructions long. LEAVE\_BLOCK leaves the block identified by
|
|
NAME. LEAVE\_BLOCK will jump to the end of the block, and is not necessary
|
|
unless you want to exit the block early.
|
|
|
|
- SET\_VALUE sym:reg, value:reg
|
|
Set the value as a variable of SYM to VALUE.
|
|
|
|
- SET\_FUNCTION sym:reg, value:reg
|
|
Set the value as a function of SYM to VALUE (value must be an actual function,
|
|
not a symbol).
|
|
|
|
- GET\_VALUE dest:reg, sym:reg
|
|
Store the value as a variable of the symbol SYM into DEST.
|
|
|
|
- GET\_FUNCTION dest:reg, sym:reg
|
|
Store the value as a function of the symbol SYM into DEST.
|
|
|
|
- BOUNDP dest:reg, sym:reg
|
|
If SYM has a value as a variable, place t in DEST, otherwise, place nil in DEST.
|
|
|
|
- FUNCTIONP dest:reg, sym:reg
|
|
If SYM has a value as a function, place t in DEST, otherwise, place nil in DEST.
|
|
|
|
- NEWFUNCTION\_LIT dest:reg, nreq:u32, nopt:u32, nkey:u32,
|
|
aok:bool, rest:bool, count:u64
|
|
NEWFUNCTION\_DYN dest:reg, nreq:u32, nopt:u32, nkey:u32,
|
|
aok:bool, rest:bool, src:reg
|
|
Create a new function object and store it into DEST. If the first case the
|
|
next COUNT instructions are considered to be the function and are skipped. In
|
|
the second case SRC should be a list or vector containing the bytecode for the
|
|
function. NREQ, NOPT, and NKEY are the number of required, optional, and key
|
|
arguments required for this function. The names of the keyword arguments are
|
|
passed in the "arg" registers. The runtime will handle normalizing these for
|
|
you. AOK is allow other keys, REST is wether to allow more positional
|
|
arguments. Arguments are passed by the runtime in "arg" registers. The first
|
|
required argument is passed in the "arg0" register. After required are
|
|
optional arguments. These are passed in two registers. The first is either nil
|
|
or t weather or not he argument was actually passed. The second is its value,
|
|
or nil if it was not passed. The same is true for key arguments. Finally if
|
|
rest is passed, any extra arguments are passed as a list as the final element.
|
|
|
|
- PUT sym:reg, key:reg, value:reg
|
|
Associate KEY with VALUE in the plist of SYM.
|
|
|
|
- GET dest:reg, sym:reg, key:reg
|
|
Store the value associated with KEY in the plist of SYM into DEST.
|
|
|
|
- AND2 dest:reg, val1:reg, val2:reg
|
|
ANDN dest:reg, count:u64
|
|
Logical and VAL1 and VAL2, or the first COUNT "arg" registers, and place the
|
|
result in DEST.
|
|
|
|
- OR2 dest:reg, val1:reg, val2:reg
|
|
ORN dest:reg, count:u64
|
|
Logical or VAL1 and VAL2, or the first COUNT "arg" registers, and place the
|
|
result in DEST.
|
|
|
|
- XOR2 dest:reg, val1:reg, val2:reg
|
|
XORN dest:reg, count:u64
|
|
Logical xor VAL1 and VAL2, or the first COUNT "arg" registers, and place the
|
|
result in DEST.
|
|
|
|
- NOT dest:reg, value:reg
|
|
Take the logical negation of VALUE and place it in DEST.
|
|
|
|
- JMP offset:i64
|
|
CJMP cond:reg, offset:i64
|
|
If the value in COND is truthy (not nil), skip the next OFFSET
|
|
instructions. If OFFSET is negative, instead go back abs(OFFSET)
|
|
instructions. CJMP is NOT counted as an instruction for the purposes of
|
|
counting offsets. Therefore an OFFSET of -2 means restart execute at the
|
|
instruction above the instruction above this CJMP. JMP is like CJMP, but takes
|
|
no condition and always jumps.
|
|
|
|
- CAR dest:reg, cons:reg
|
|
CDR dest:reg, cons:reg
|
|
Store the car or cdr of CONS into DEST.
|
|
|
|
- SETCAR cons:reg, value:reg
|
|
SETCDR cons:reg, value:reg
|
|
Store VALUE into the car or cdr of CONS.
|
|
|
|
- GETELT\_LIT dest:reg, seq:reg, index:u64
|
|
GETELT\_DYN dest:reg, seq:reg, index:reg
|
|
Store the value at INDEX in SEQ (a list, string, or vector) into DEST.
|
|
|
|
- SETELT\_LIT seq:reg, index:u64, value:reg
|
|
SETELT\_DYN seq:reg, index:reg, value:reg
|
|
Store VALUE into the index numbered INDEX of SEQ (a list, string, or vector).
|
|
|
|
- EQ dest:reg, val1:reg, val2:reg
|
|
Compare VAL1 and VAL2, if they are the same object (or symbols with the same
|
|
name) store T into DEST, otherwise, store NIL.
|
|
|
|
- NUM\_GT dest:reg, val1:reg, val2:reg
|
|
NUM\_GE dest:reg, val1:reg, val2:reg
|
|
NUM\_EQ dest:reg, val1:reg, val2:reg
|
|
NUM\_LE dest:reg, val1:reg, val2:reg
|
|
NUM\_LT dest:reg, val1:reg, val2:reg
|
|
Compare VAL1 and VAL2, which must be numbers, and compare their values. If
|
|
they pass, store T into DEST, otherwise store NIL.
|
|
|
|
- ADD dest:reg, count:u64
|
|
Add the first COUNT "arg" registers and place the result into DEST.
|
|
|
|
- SUB dest:reg, val1:reg, val2:reg
|
|
Subtract the sum of the first COUNT - 1 "arg" registers starting from "arg1",
|
|
that is "arg1", "arg2", "arg3", etc., from "arg0". Store the result in DEST.
|
|
|
|
- MUL dest:reg, count:u64
|
|
Multiply the first COUNT "arg" registers and place the result in DEST.
|
|
|
|
- DIV dest:reg, count:u64
|
|
Divide "arg0" by the product of the COUNT - 1 "arg" registers starting from "arg1"
|
|
|
|
- INT_DIV dest:reg, val1:reg, val2:reg
|
|
Divide VAL1 by VAL2 and place the integer part of the result in DEST.
|
|
|
|
- RECIP dest:reg, val:reg
|
|
Divide 1 by VAL and place the result in DEST.
|
|
|
|
- MOD dest:reg, val1:reg, val2:reg
|
|
Divide VAL1 by VAL2 and place the remainder in DEST.
|
|
|
|
- SQRT dest:reg, val:reg
|
|
Take the square root of VAL and place it in DEST.
|
|
|
|
- POW dest:reg, base:reg, exp:reg
|
|
Raise BASE to the EXP power and place the result in DEST.
|
|
|
|
- LN dest:reg, val:reg
|
|
Take the natural log of VAL and place the result in DEST.
|
|
|
|
- EXP dest:reg, val:reg
|
|
Take the exponential function at VAL and place the result in DEST.
|
|
|
|
- SIN dest:reg, val:reg
|
|
COS dest:reg, val:reg
|
|
TAN dest:reg, val:reg
|
|
Take the sine, cosine, or tangent of VAL (in radians) and place it into DEST.
|
|
|
|
- ASIN dest:reg, val:reg
|
|
ACOS dest:reg, val:reg
|
|
ATAN dest:reg, val:reg
|
|
Take the inverse sine, cosine, or tangent of VAL and place the result (in
|
|
radians) into DEST.
|
|
|
|
- BITAND dest:reg, val1:reg, val2:reg
|
|
BITOR dest:reg, val1:reg, val2:reg
|
|
BITXOR dest:reg, val1:reg, val2:reg
|
|
BITNOR dest:reg, val1:reg, val2:reg
|
|
Perform the given bit-wise operation on VAL1 and VAL2 and place the result
|
|
into DEST.
|
|
|
|
- BITNEG dest:reg, val:reg
|
|
Negate each bit in VAL. That is, flip each bit in VAL.
|
|
|
|
- LSH dest:reg, val:reg, by:reg
|
|
ASH dest:reg, val:reg, by:reg
|
|
Either logically or arithmetically shift VAL by BY bits to the left (or right
|
|
is BY is negative). Put the result in DEST.
|
|
|