Implemented many operations
This commit is contained in:
parent
a89fe4f0c6
commit
13f9330eea
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
build/
|
||||
arctic
|
||||
|
10
README.md
10
README.md
|
@ -3,3 +3,13 @@
|
|||
Like the JVM, but the "binaries" consist of printable ASCII characters and the
|
||||
result can be more easily precompiled for native execution.
|
||||
|
||||
## TODO
|
||||
|
||||
- multiprocessing support (call-through to fork()? codeN for each process?)
|
||||
- probably want to repurpose breakpoint into fork
|
||||
- overall philosophy
|
||||
- easy to target for compilers
|
||||
- easy to implement with overhead
|
||||
- possible to implement with no overhead
|
||||
- can use easy but "slow" or DIY
|
||||
- e.g. MALL vs static memory and custom allocation
|
||||
|
|
7
doc/documentation.css
Normal file
7
doc/documentation.css
Normal file
|
@ -0,0 +1,7 @@
|
|||
#instructions ~ table td:first-child {
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
#instructions ~ table td:first-child + td {
|
||||
font-family: monospace;
|
||||
}
|
|
@ -1,9 +1,86 @@
|
|||
# ASCII Rendered, Compiled Target Invocation Code
|
||||
---
|
||||
Title: ARCTIC Documentation
|
||||
Author: Louis A. Burke
|
||||
CSS: documentation.css
|
||||
---
|
||||
|
||||
# ARCTIC Code
|
||||
|
||||
ASCII Rendered, Cross-Target Invocation Character Codes
|
||||
|
||||
Like the JVM, but the "binaries" consist of printable ASCII characters and the
|
||||
result can be more easily precompiled for native execution.
|
||||
result can be more easily precompiled for native execution. Also far less safety
|
||||
and verification, feel free to shoot yourself in the foot!
|
||||
|
||||
## Opcodes
|
||||
## Structure
|
||||
|
||||
### Data Types
|
||||
|
||||
### The Stack
|
||||
|
||||
### Memory
|
||||
|
||||
### Sections
|
||||
|
||||
```{=todo}
|
||||
code is LE \ w.r.t
|
||||
CODE is BE / LDAx
|
||||
|
||||
Specifically:
|
||||
|
||||
A.* is BE atomic data
|
||||
a.* is LE atomic data
|
||||
[bB].* is BSS data
|
||||
C.* is BE code
|
||||
c.* is LE code
|
||||
D.* is BE data
|
||||
d.* is LE data
|
||||
E.* is BE externals
|
||||
e.* is LE externals
|
||||
[fF].* is embedded file ("file X")
|
||||
[gG].* is embedded resource ("get X")
|
||||
[hH].* is embedded resource ("http...")
|
||||
[iI].* is embedded code ("include X")
|
||||
[lL].* is embedded arctic file ("load X")
|
||||
M.* is BE macros
|
||||
m.* is LE macros
|
||||
P.* is BE per-thread data (POOL)
|
||||
p.* is LE per-thread data (pool)
|
||||
R.* is BE read-only data
|
||||
r.* is LE read-only data
|
||||
[wW].* is load module ("with X")
|
||||
```
|
||||
|
||||
## File Format
|
||||
|
||||
## Loading, Compiling, and Running
|
||||
|
||||
## Instructions
|
||||
|
||||
This section describes the various instructions that are defined for an ARCTIC
|
||||
system. When stack manipulation is shown, the following conventions apply.
|
||||
|
||||
*Stack direction:* The stack is shown from left to right where the top of the
|
||||
stack is on the right. For example `1 2 3` shows a stack with 3 on top of 2 on
|
||||
top of 1.
|
||||
|
||||
*Ellipses:* When only the top of the stack is relevant, the rest may be
|
||||
abbreviated with `…`. For example `… 10` shows a stack with a value of 10 at the
|
||||
top and unknown data possibly below it.
|
||||
|
||||
*Production:* To indicate the change in a stack, an arrow is used. For example
|
||||
`1 2 3 → 3 1 2` shows a stack containing 1, 2, 3 becoming a stack containing 3,
|
||||
1, 2. Similarly, if the arrow is doubled, it indicates an immediate value is
|
||||
also consumed. For example `1 2 ⇒ 3` shows a stack containing 1 and 2 becoming
|
||||
a stack containing 3 after consuming an immediate value.
|
||||
|
||||
*Variables/Values:* Variables and values are presented as space-separated
|
||||
c expressions. These are interpreted as if declared as follows:
|
||||
|
||||
int64_t x, y, z;
|
||||
double a, b, c;
|
||||
void *p, *q; // data pointers
|
||||
void *f; // function pointer
|
||||
|
||||
```{=todo}
|
||||
Notate that immediates can be: …###, …###.###, …$name, or …v (for abcinxyz)
|
||||
|
@ -22,22 +99,490 @@ The ops below are probably code exclusive. The remaining " and ' can switch
|
|||
modes? Maybe just "section" to start a section? Leaving ' as the only impl-def?
|
||||
```
|
||||
|
||||
|
||||
### Memory
|
||||
|
||||
The following table summarizes the memory operations that are described below.
|
||||
|
||||
⋄ |Code| … | Stack | … | Description
|
||||
--|---:|----------:|:-:|:----------------|:-------------------------------------
|
||||
1 |LDAB| … p| → | … x | load byte x from p
|
||||
2 |LDAS| … p| → | … x | load short x from p
|
||||
3 |LDAI| … p| → | … x | load int x from p
|
||||
4 |LDAW| … p| → | … x | load word x from p
|
||||
o |LDOB| … p| ⇒ | … x | load byte at p plus immediate offset in x
|
||||
H |LDOS| … p| ⇒ | … x | load short at p plus immediate offset in x
|
||||
G |LDOI| … p| ⇒ | … x | load int at p plus immediate offset in x
|
||||
Q |LDOW| … p| ⇒ | … x | load word at p plus immediate offset in x
|
||||
5 |STAB| … p x| → | … | store byte x to p
|
||||
6 |STAS| … p x| → | … | store short x to p
|
||||
7 |STAI| … p x| → | … | store int x to p
|
||||
8 |STAW| … p x| → | … | store word x to p
|
||||
D |MCLR| … p x| → | … | memclear x bytes from p
|
||||
d |MSET| … p x| ⇒ | … | set x bytes of memory to immediate value at p
|
||||
K |MCPY| … p q x| → | … | copy x bytes of memory from p to q
|
||||
J |MOFF| … p x| → | … q | set q to memory pointer p shifted by x bytes
|
||||
M |MALL| … p| ⇒ | … p | (re)allocate/free memory for immediate bytes at p
|
||||
R |REAL| … p x| → | … p | (re)allocate/free memory for x bytes at p
|
||||
9 |CASS| … p x y| → | … z | compare and swap x and y at p, return success
|
||||
|
||||
#### LDAx
|
||||
|
||||
The LDAx instructions take a pointer on the top of the stack and replace it with
|
||||
the value of 1, 2, 4, or 8 bytes loaded from its address as 2's compliment. The
|
||||
endianness is determined by the endianness of the code section under execution.
|
||||
|
||||
- LDAB: `… p → … (int64)*(int8*)p`
|
||||
- LDAS: `… p → … (int64)*(int16*)p`
|
||||
- LDAI: `… p → … (int64)*(int32*)p`
|
||||
- LDAW: `… p → … *(int64*)p`
|
||||
|
||||
#### LDOx
|
||||
|
||||
The LDOx instructions take an immediate offset following the instruction code
|
||||
and a pointer on top of the stack and act like their LDAx counterparts, but
|
||||
operating on the memory address specified by adding the immediate value to the
|
||||
pointer.
|
||||
|
||||
- LDOB(i): `… p ⇒ … (int64)((int8*)p)[i]`
|
||||
- LDOS(i): `… p ⇒ … (int64)((int16*)p)[i]`
|
||||
- LDOI(i): `… p ⇒ … (int64)((int32*)p)[i]`
|
||||
- LDOW(i): `… p ⇒ … ((int64*)p)[i]`
|
||||
|
||||
|
||||
#### STAx
|
||||
|
||||
The STAx instructions take a value on top of the stack and a pointer underneath
|
||||
it and write the least significant 1, 2, 4, or 8 bytes of the value to the
|
||||
address of the pointer, removing both from the stack.
|
||||
|
||||
- STAB: `… p x → … (void)(*(int8*)p=x&0xFF)`
|
||||
- STAS: `… p x → … (void)(*(int16*)p=x&0xFFFF)`
|
||||
- STAI: `… p x → … (void)(*(int32*)p=x&0xFFFFFFFF)`
|
||||
- STAW: `… p x → … (void)(*p=x)`
|
||||
|
||||
#### MOFF
|
||||
|
||||
The MOFF instruction takes an integer on top of the stack and a memory address
|
||||
underneath it and shifts the memory address by the integer, removing the integer
|
||||
in the process.
|
||||
|
||||
- MOFF: `… p x → … ((int8*p)+x)`
|
||||
|
||||
#### MCLR
|
||||
|
||||
The MCLR instruction takes an integer value on top of the stack and a pointer
|
||||
underneath it. It then clears as many bytes of memory in order as indicated by
|
||||
the integer value starting from the pointer address. Both the value and pointer
|
||||
are removed from the stack.
|
||||
|
||||
- MCLR: `… p x → … (void)memset(p,x,0)`
|
||||
|
||||
#### MSET
|
||||
|
||||
The MSET instruction takes an immediate byte value following the instruction
|
||||
code, as well as an integer value on top of the stack and a pointer underneath
|
||||
it. It then sets as many bytes of memory in order as indicated by the integer
|
||||
value starting from the pointer address to the immediate value. Both the value
|
||||
and pointer are removed from the stack.
|
||||
|
||||
- MSET(i): `… p x ⇒ … (void)memset(p,x,i)`
|
||||
|
||||
#### MCPY
|
||||
|
||||
The MCPY instruction takes an integer value on top of the stack and a pair of
|
||||
pointers underneath it. It then copies as many bytes of memory in order as
|
||||
indicated by the integer value starting from the bottom pointer into memory
|
||||
starting from the top pointer. All three arguments are removed from the stack.
|
||||
|
||||
- MCPY: `… p q x → … (void)memmove(q, p, x)`
|
||||
|
||||
#### MALL
|
||||
|
||||
The MALL instruction takes an immediate offset following the instruction code
|
||||
and a pointer on top of the stack and allocates or reallocates memory of that
|
||||
many bytes, replacing the pointer with the new pointer on the stack.
|
||||
|
||||
If the size is zero, this frees the memory and pushes a null pointer.
|
||||
|
||||
- MALL(i): `… p ⇒ … realloc(p, i)`
|
||||
|
||||
#### REAL
|
||||
|
||||
The REAL instruction takes an offset value on top of the stack and a pointer
|
||||
underneath it and allocates or reallocates memory of that many bytes, replacing
|
||||
the pointer with the new pointer on the stack.
|
||||
|
||||
If the size is zero, this frees the memory and pushes a null pointer.
|
||||
|
||||
- REAL: `… p x → … realloc(p, x)`
|
||||
|
||||
#### CASS
|
||||
|
||||
The CASS instruction takes a new value on top of the stack, an expected old
|
||||
value underneath it, and a pointer underneath that. It atomically compares the
|
||||
8 bytes of memory referenced by the pointer with the expected old value; if they
|
||||
match, the memory content is updated to the new value and all three arguments
|
||||
are replaced with the number 1 as a 64-bit integer; if they do not match, no
|
||||
memory is updated and all three arguments are replaced with the number 0.
|
||||
|
||||
- CASS: `… p x y → … atomic_compare_exchange_strong(p,&x,y)`
|
||||
|
||||
### Math
|
||||
|
||||
⋄ |Code| … | Stack | … | Description
|
||||
--|---:|----------:|:-:|:----------------|:-------------------------------------
|
||||
\+|IADD| … x y| → | … z | set z to the sum of x and y
|
||||
\-|ISUB| … x y| → | … z | set z to the difference between x and y (x - y)
|
||||
\*|IMUL| … x y| → | … z | set z to the product of x and y
|
||||
/ |IDIV| … x y| → | … z | set z to the quotient of x by y
|
||||
% |IMOD| … x y| → | … z | set z to the modulo of x by y
|
||||
\\|IREM| … x y| → | … z | set z to the remainder of x by y
|
||||
\_|INEG| … x| → | … x | negate x
|
||||
\||IABS| … x| → | … x | take the absolute value of x
|
||||
s |FADD| … a b| → | … c | set c to the sum of a and b
|
||||
m |FSUB| … a b| → | … c | set c to the difference between a and b (a - b)
|
||||
p |FMUL| … a b| → | … c | set c to the product of a and b
|
||||
q |FDIV| … a b| → | … c | set c to the quotient of a by b
|
||||
f |FMOD| … a b| → | … c | set c to the modulo of a by b
|
||||
r |FREM| … a b| → | … c | set c to the remainder of a by b
|
||||
j |FNEG| … a| → | … a | negate a
|
||||
v |FABS| … a| → | … a | take the absolute value of a
|
||||
V |BIOR| … x y| → | … z | bitwise OR x and y and store in z
|
||||
\&|BAND| … x y| → | … z | set z to the bitwise and of x and y
|
||||
^ |BXOR| … x y| → | … z | set z to the bitwise xor of x and y
|
||||
l |BITC| … x y| → | … z | bit clear y from x to z (z = x and not y)
|
||||
! |BNOT| … x| → | … y | set y to the bitwise not of x
|
||||
u |USHR| … x| ⇒ | … x | logical shift x by immediate bits right
|
||||
[ |ROTR| … x| ⇒ | … x | rotate x by immediate bits
|
||||
] |BSHR| … x| ⇒ | … x | arithmetic right shift x by immediate bits
|
||||
? |CMPI| … x y| → | … z | compare x to y and set z such that x o y is z o 0
|
||||
~ |CMPF| … a b| ⇒ | … c | compare a to b and set c such that a o b is z o c within immediate ULPs
|
||||
U |CMPU| … x y| → | … z | compare x to y unsigned and set z such that x o y is z o 0
|
||||
F |BMIS| … x| ⇒ | … x | bit manipulation instructions
|
||||
|
||||
#### xADD/xSUB/xMUL/xDIV/xMOD/xREM
|
||||
|
||||
The xADD/xSUB/xMUL/xDIV/xMOD/xREM instructions take two values on top of the
|
||||
stack (either two integers or two floating point numbers) and perform the
|
||||
specified mathematical operation, replacing the values with the result of the
|
||||
operation.
|
||||
|
||||
The difference between MOD and REM is that MOD is floored while REM is
|
||||
truncated.
|
||||
|
||||
- IADD: `… x y → (x+y)`
|
||||
- FADD: `… a b → (a+b)`
|
||||
- ISUB: `… x y → (x-y)`
|
||||
- FSUB: `… a b → (a-b)`
|
||||
- IMUL: `… x y → (x*y)`
|
||||
- FMUL: `… a b → (a*b)`
|
||||
- IDIV: `… x y → (x/y)`
|
||||
- FDIV: `… a b → (a/b)`
|
||||
- IMOD: `… x y → (y>0?abs(x%y):-abs(x%y))`
|
||||
- FMOD: `… a b → (b>0?fabs(fmod(x,y)):-fabs(fmod(x,y)))`
|
||||
- IREM: `… x y → x%y`
|
||||
- FREM: `… a b → fmod(x,y)`
|
||||
|
||||
#### xNEG/xABS
|
||||
|
||||
The xNEG/xABS instructions take a value on top of the stack (either an integer
|
||||
or a floating point number) and replaces it with either its negated value or its
|
||||
absolute value.
|
||||
|
||||
- INEG: `… x → -x`
|
||||
- FNEG: `… a → -a`
|
||||
- IABS: `… x → abs(x)`
|
||||
- FABS: `… a → fabs(a)`
|
||||
|
||||
#### BIOR/BAND/BXOR/BITC
|
||||
|
||||
The BIOR/BAND/BXOR/BITC instructions take two values on top of the stack and
|
||||
perform the specified bitwise operation, replacing the values with the result of
|
||||
the operation.
|
||||
|
||||
BITC is a single instruction that performs A AND NOT B between its arguments.
|
||||
|
||||
- BIOR: `… x y → x|y`
|
||||
- BAND: `… x y → x&y`
|
||||
- BXOR: `… x y → x^y`
|
||||
- BITC: `… x y → x&~y`
|
||||
|
||||
#### USHR/BSHR/ROTR
|
||||
|
||||
The USHR/BSHR instructions take a value on top of the stack and an
|
||||
immediate offset and shift the value on top of the stack that number of bits in
|
||||
the given direction.
|
||||
|
||||
USHR and BSHR differ in that BSHR performs an arithmetic shift, duplicating the
|
||||
highest order bit instead of shifting in a 0.
|
||||
|
||||
ROTR performs a rightward rotation, however with a negative shift amount, it
|
||||
performs a leftward rotation.
|
||||
|
||||
- USHR(i): `… x ⇒ ((uint64_t)x)>>i`
|
||||
- BSHR(i): `… x ⇒ x>>i`
|
||||
- ROTR(i): `… x ⇒ (x>>shift)|(value<<(64-shift))`
|
||||
|
||||
#### CMPx
|
||||
|
||||
The CMPx instructions perform comparisons between two values on top of the stack
|
||||
and replace them with either 1, 0, or -1 if the top value is less than, equal
|
||||
to, or greater than the second value respectively. In the case of CMPF,
|
||||
a floating point precision is specified by an immediate value, with equality
|
||||
spanning all values within the given precision of each other.
|
||||
|
||||
- CMPI: `… x y → ((x-y)>0)-((x-y)<0)`
|
||||
- CMPF(i): `… a b ⇒ fabs(a-b)<i?0:((a-b)>0)-((a-b)<0)`
|
||||
- CMPU: `… x y → (((uint64_t)x-(uint64_t)y)>0)-(((uint64_t)x-(uint64_t)y)<0)`
|
||||
|
||||
#### BMIS
|
||||
|
||||
The BMIS instruction bundles many unary bitwise operators into a single
|
||||
instruction, indexed by an immediate integer based on the following table:
|
||||
|
||||
Immediate | Operation
|
||||
-----------|--------------------------------------------------------------------
|
||||
0 | ???
|
||||
|
||||
### Type Conversions
|
||||
|
||||
⋄ |Code| … | Stack | … | Description
|
||||
--|---:|----------:|:-:|:----------------|:-------------------------------------
|
||||
E |FTOI| … a| ⇒ | … x | round a to integer and store in x
|
||||
O |ITOC| … x| → | … x | truncate x to 8 bits, then sign extend
|
||||
S |ITOS| … x| → | … x | truncate x to 16 bits, then sign extend
|
||||
W |ITOW| … x| → | … x | truncate x to 32 bits, then sign extend
|
||||
. |ITOF| … x| → | … a | convert x to a floating point number
|
||||
|
||||
#### FTOI
|
||||
|
||||
The FTOI instruction takes a floating point value on top of the stack and
|
||||
replaces it with an integer by rounding. The direction of rounding is determined
|
||||
by the immediate integer value:
|
||||
|
||||
Value | Rounding Mode
|
||||
------:|:-----------------------------------------------------------------------
|
||||
1 | Round towards infinity. "Round up"
|
||||
-1 | Round towards negative infinity. "Round down"
|
||||
0 | Round towards 0. "Truncate"
|
||||
others| Round to even. "Unbiased"
|
||||
|
||||
- FTOI(1): `… a ⇒ fesetround(FE_UPWARD),(int64_t)a`
|
||||
- FTOI(-1): `… a ⇒ fesetround(FE_DOWNWARD),(int64_t)a`
|
||||
- FTOI(0): `… a ⇒ fesetround(FE_TOWARDZERO),(int64_t)a`
|
||||
- FTOI(i): `… a ⇒ fesetround(FE_TONEAREST),(int64_t)a`
|
||||
|
||||
#### ITOC/ITOS/ITOW
|
||||
|
||||
The ITOC/ITOS/ITOW instructions take a 64-bit integer on top of the stack and
|
||||
replace it with a truncated and sign extended smaller integer.
|
||||
|
||||
- ITOC: `… x → (int64_t)*(int8_t*)&x`
|
||||
- ITOS: `… x → (int64_t)*(int16_t*)&x`
|
||||
- ITOW: `… x → (int64_t)*(int32_t*)&x`
|
||||
|
||||
#### ITOF
|
||||
|
||||
The ITOF instruction takes a 64-bit integer on top of the stack and replaces it
|
||||
with the closest 64-bit floating point approximation.
|
||||
|
||||
- ITOF: `… x → (double)x`
|
||||
|
||||
### Stack Management
|
||||
|
||||
⋄ |Code| … | Stack | … | Description
|
||||
--|---:|----------:|:-:|:----------------|:-------------------------------------
|
||||
w |SDUP| … x| → | … x x | stack duplicate
|
||||
k |SPOP| … x| → | … | stack pop
|
||||
t |SWAP| … x y| → | … y x | stack swap
|
||||
h |OVER| … x y| → | … x y x | stack over
|
||||
e |SROT| … x y z| → | … y z x | rotate stack
|
||||
P |PICK| … x ‥ z| ⇒ | … x ‥ z x | pick stack element at immediate place
|
||||
L |ROLL| … x y ‥ z| ⇒ | … y ‥ z x | roll stack by immediate places
|
||||
T |PUSH| … x ‥ y z| ⇒ | … y x ‥ z | push top of stack down by immediate places (opposite of ROLL)
|
||||
( |DPTH| …| → | … x | set x to depth of stack (before x)
|
||||
) |PACK| … p x| → | … p | pack x elements of stack (before p) into array p
|
||||
|
||||
#### SDUP
|
||||
|
||||
The SDUP instruction takes a value on top of the stack and pushes a copy of it
|
||||
to the top of the stack.
|
||||
|
||||
- SDUP: `… x → x x`
|
||||
|
||||
#### SPOP
|
||||
|
||||
The SPOP instruction removes the value on top of the stack.
|
||||
|
||||
- SPOP: `… x → `
|
||||
|
||||
#### SWAP/OVER/SROT
|
||||
|
||||
The SWAP/OVER/SROT instructions manipulate the values near the top of the stack.
|
||||
|
||||
- SWAP: `… x y → … y x`
|
||||
- OVER: `… x y → … x y x`
|
||||
- SROT: `… x y z → … y z x`
|
||||
|
||||
#### PICK
|
||||
|
||||
The PICK instruction acts like an OVER command with an arbitrary depth, copying an
|
||||
element indicated by the immediate value to the top of the stack. OVER can be
|
||||
defined in terms of PICK as `P1` while SDUP is equivalent to `P0`.
|
||||
|
||||
- PICK(i): `… x …i… y ⇒ … x …i… y x`
|
||||
|
||||
#### ROLL/PUSH
|
||||
|
||||
The ROLL and PUSH instructions act like SROT with an arbitrary depth, allowing
|
||||
a value from any depth in the stack to be rotated to the top or pushed back down
|
||||
to depth. SROT can be defined in terms of ROLL as `L1` while SWAP is equivalent
|
||||
to `L0` or `T0`.
|
||||
|
||||
- ROLL(i): `… x y …i… z ⇒ … y …i… z x`
|
||||
- PUSH(i): `… x …i… y z ⇒ … z x …i… y`
|
||||
|
||||
#### DPTH/PACK
|
||||
|
||||
The DPTH instruction pushes the total depth of the stack on top of the stack.
|
||||
The PACK instruction takes an integer on top of a pointer and saves the top
|
||||
elements of the stack beneath the pointer to the memory pointed to by it.
|
||||
|
||||
These can be combined to store the entire stack in a new array via: `(w$0 Rt)`
|
||||
|
||||
- DPTH: `…x… → …x… x`
|
||||
- PACK: `… p x → p`
|
||||
|
||||
### Control Flow
|
||||
|
||||
⋄ |Code| … | Stack | … | Description
|
||||
--|---:|----------:|:-:|:----------------|:-------------------------------------
|
||||
\=|BEQZ| … x| ⇒ | … | branch to immediate if x is zero
|
||||
0 |BNEZ| … x| ⇒ | … | branch to immediate if x not zero
|
||||
\<|BLTZ| … x| ⇒ | … | branch to immediate if x less than zero
|
||||
\>|BGTZ| … x| ⇒ | … | branch to immediate if x is greater than zero
|
||||
{ |BLEZ| … x| ⇒ | … | branch to immediate if x is less than or equal to zero
|
||||
} |BGEZ| … x| ⇒ | … | branch to immediate if x is greater or equal to zero
|
||||
g |FINV| … f| → | f … | invoke f, saving return address on stack
|
||||
@ |CALL| …| → | f … | call immediate name
|
||||
, |JUMP| …| ⇒ | … | jump to immediate without pushing return address
|
||||
; |RETN| … f| → | … | return from subroutine (jump but for stack)
|
||||
\:|LABL| … | ↔ | … | label code location
|
||||
|
||||
#### BEQZ/BNEZ/BLTZ/BGTZ/BLEZ/BGEZ
|
||||
|
||||
The BEQZ/BNEZ/BLTZ/BGTZ/BLEZ/BGEZ instructions are the basic branching
|
||||
instructions. They take an immediate offset and, if their condition is met, jump
|
||||
that many beats (lines) forward or backward.
|
||||
|
||||
- BEQZ(i): `… x ⇒ …`
|
||||
- BNEZ(i): `… x ⇒ …`
|
||||
- BLTZ(i): `… x ⇒ …`
|
||||
- BGTZ(i): `… x ⇒ …`
|
||||
- BLEZ(i): `… x ⇒ …`
|
||||
- BGEZ(i): `… x ⇒ …`
|
||||
|
||||
#### FINV/CALL
|
||||
|
||||
The FINV/CALL instructions invoke functions by jumping to their first
|
||||
instruction after pushing the address of the next instruction on the stack.
|
||||
|
||||
- FINV: `… f → g …`
|
||||
- CALL(f): `… → g …`
|
||||
|
||||
#### JUMP/RETN
|
||||
|
||||
The JUMP/RETN instructions are the complement to the FINV/CALL instructions.
|
||||
They jump to the first instruction of the given function, but do not push
|
||||
a return address on the stack.
|
||||
|
||||
- JUMP(f): `… ⇒ …`
|
||||
- RETN: `… f ⇒ …`
|
||||
|
||||
#### LABL
|
||||
|
||||
The LABL instruction takes an immediate name and marks the next current beat in
|
||||
memory as having that name for future or past references.
|
||||
|
||||
- LABL: `… ↔ …`
|
||||
|
||||
### Variables
|
||||
|
||||
⋄ |Code| … | Stack | … | Description
|
||||
--|---:|----------:|:-:|:----------------|:-------------------------------------
|
||||
A |PUTA| … x| → | … | store x in rA
|
||||
B |PUTB| … x| → | … | store x in rB
|
||||
C |PUTC| … x| → | … | store x in rC
|
||||
I |PUTI| … x| → | … | store x in rI
|
||||
N |PUTN| … x| → | … | store x in rN
|
||||
X |PUTX| … x| → | … | store x in rX
|
||||
Y |PUTY| … x| → | … | store x in rY
|
||||
Z |PUTZ| … x| → | … | store x in rZ
|
||||
a |GETA| …| → | … x | load x from rA
|
||||
b |GETB| …| → | … x | load x from rB
|
||||
c |GETC| …| → | … x | load x from rC
|
||||
i |GETI| …| → | … x | load x from rI
|
||||
n |GETN| …| → | … x | load x from rN
|
||||
x |GETX| …| → | … x | load x from rX
|
||||
y |GETY| …| → | … x | load x from rY
|
||||
z |GETZ| …| → | … x | load x from rZ
|
||||
|
||||
#### PUTA/PUTB/PUTC/PUTI/PUTN/PUTX/PUTY/PUTZ
|
||||
|
||||
The PUTA/PUTB/PUTC/PUTI/PUTN/PUTX/PUTY/PUTZ instructions take a value on top of
|
||||
the stack and store it in a register for later recall.
|
||||
|
||||
- PUTA: `… x → …`
|
||||
- PUTB: `… x → …`
|
||||
- PUTC: `… x → …`
|
||||
- PUTI: `… x → …`
|
||||
- PUTN: `… x → …`
|
||||
- PUTX: `… x → …`
|
||||
- PUTY: `… x → …`
|
||||
- PUTZ: `… x → …`
|
||||
|
||||
#### GETA/GETB/GETC/GETI/GETN/GETX/GETY/GETZ
|
||||
|
||||
The GETA/GETB/GETC/GETI/GETN/GETX/GETY/GETZ instructions push the previously
|
||||
saved value in a register onto the stack.
|
||||
|
||||
- GETA: `… → … x`
|
||||
- GETB: `… → … x`
|
||||
- GETC: `… → … x`
|
||||
- GETI: `… → … x`
|
||||
- GETN: `… → … x`
|
||||
- GETX: `… → … x`
|
||||
- GETY: `… → … x`
|
||||
- GETZ: `… → … x`
|
||||
|
||||
### Miscellaneous
|
||||
|
||||
⋄ |Code| … | Stack | … | Description
|
||||
--|---:|----------:|:-:|:----------------|:-------------------------------------
|
||||
$ |VALU| … | ⇒ | … \* | load constant value
|
||||
' |DATA| | | | Embed data
|
||||
" |SECT| | | | Change section
|
||||
\`|BIFC| … ? | ↔ | … ? | call builtin with given name/value
|
||||
␣ |NOOP| … | ↔ | … | do nothing, maybe end identifier definition
|
||||
¶ |BEAT| … | ↔ | … | mark a beat for relative branching
|
||||
|
||||
### Summary
|
||||
|
||||
| -0 | -1 | -2 | -3 | -4 | -5 | -6 | -7
|
||||
---:|:------:|:------:|:------:|:------:|:------:|:------:|:------:|:------:
|
||||
000-|`0 BNEZ`|`1 LDAB`|`2 LDAS`|`3 LDAI`|`4 LDAW`|`5 STAB`|`6 STAS`|`7 STAI`
|
||||
001-|`8 STAW`|`9 CASS`|`A PUTA`|`B PUTB`|`C PUTC`|`D MCLR`|`E FTOI`|`F FREE`
|
||||
010-|`G LDOI`|`H LDOS`|`I PUTI`|`J MOFF`|`K MCPY`|`L ROLL`|`M MNEW`|`N PUTN`
|
||||
011-|`O ITOC`|`P PICK`|`Q LDOW`|`R MALL`|`S ITOS`|`T PUSH`|`U CMPU`|`V B_OR`
|
||||
001-|`8 STAW`|`9 CASS`|`A PUTA`|`B PUTB`|`C PUTC`|`D MCLR`|`E FTOI`|`F BMIS`
|
||||
010-|`G LDOI`|`H LDOS`|`I PUTI`|`J MOFF`|`K MCPY`|`L ROLL`|`M MALL`|`N PUTN`
|
||||
011-|`O ITOC`|`P PICK`|`Q LDOW`|`R REAL`|`S ITOS`|`T PUSH`|`U CMPU`|`V BIOR`
|
||||
100-|`W ITOW`|`X PUTX`|`Y PUTY`|`Z PUTZ`|`a GETA`|`b GETB`|`c GETC`|`d MSET`
|
||||
101-|`e SROT`|`f FMOD`|`g FINV`|`h OVER`|`i GETI`|`j FNEG`|`k SPOP`|`l BITC`
|
||||
110-|`m FSUB`|`n GETN`|`o LDOB`|`p FMUL`|`q FDIV`|`r FREM`|`s FADD`|`t SWAP`
|
||||
111-|`u USHR`|`v FABS`|`w SDUP`|`x GETX`|`y GETY`|`z GETZ`|`_ INEG`|`. ITOF`
|
||||
200-|`+ IADD`|`- ISUB`|`* IMUL`|`/ IDIV`|`% IMOD`|`\ IREM`|`| IABS`|`$ VALU`
|
||||
201-|`@ CALL`|`< BLTZ`|`{ BLEZ`|`= BEQZ`|`} BGEZ`|`> BGTZ`|`, JUMP`|`; RTRN`
|
||||
210-|`& BAND`|`^ BXOR`|`! BNOT`|`[ BSHL`|`] BSHR`|`? CMPI`|`~ CMPF`|`: LABL`
|
||||
211-|`( DPTH`|`) PACK`|`' DATA`|`" SECT`|`\` BKPT`|`␣ NOP`|`# COMM`|`¶ BEAT`
|
||||
210-|`& BAND`|`^ BXOR`|`! BNOT`|`[ ROTR`|`] BSHR`|`? CMPI`|`~ CMPF`|`: LABL`
|
||||
211-|`( DPTH`|`) PACK`|`' DATA`|`" SECT`|`\` BIFC`|`␣ NOP`|`# COMM`|`¶ BEAT`
|
||||
[Operations by code][ops-by-code]
|
||||
|
||||
In this table x, y, and z are integers; a, b, and c are floating point numbers;
|
||||
|
@ -45,101 +590,101 @@ p and q are memory pointers; and f is a function pointer. Additionally,
|
|||
→ indicates a basic operation, while ⇒ indicates an operation using an immediate
|
||||
value, and ↔ indicates a meta operation.
|
||||
|
||||
Code | Stack | Description
|
||||
-----:|:-------------------:|:--------------------------------------------------
|
||||
0 BNEZ| … x ⇒ … | branch to immediate if x not zero
|
||||
1 LDAB| … p → … x | load byte x from p
|
||||
2 LDAS| … p → … x | load short x from p
|
||||
3 LDAI| … p → … x | load int x from p
|
||||
4 LDAW| … p → … x | load word x from p
|
||||
5 STAB| … p x → … | store byte x to p
|
||||
6 STAS| … p x → … | store short x to p
|
||||
7 STAI| … p x → … | store int x to p
|
||||
8 STAW| … p x → … | store word x to p
|
||||
9 CASS| … p x y → … z | compare and swap x and y at p, return success
|
||||
A PUTA| … x → … | store x in rA
|
||||
B PUTB| … x → … | store x in rB
|
||||
C PUTC| … x → … | store x in rC
|
||||
D MCLR| … p x → … | memclear x bytes from p
|
||||
E FTOI| … a → … x | round a to integer and store in x
|
||||
F FREE| … p → … | free memory at p
|
||||
G LDOI| … p ⇒ … x | load int at p plus immediate offset in x
|
||||
H LDOS| … p ⇒ … x | load short at p plus immediate offset in x
|
||||
I PUTI| … x → … | store x in rI
|
||||
J MOFF| … p x → … q | set q to memory pointer p shifted by x bytes
|
||||
K MCPY| … p q x → … | copy x bytes of memory from p to q
|
||||
L ROLL| … x y ‥ z ⇒ … y ‥ z x | roll stack by immediate places
|
||||
M MNEW| … ⇒ … p | allocate memory for immediate bytes at p
|
||||
N PUTN| … x → … | store x in rN
|
||||
O ITOC| … x → … x | truncate x to 8 bits, then sign extend
|
||||
P PICK| … x ‥ z ⇒ … x ‥ z x | pick stack element at immediate place
|
||||
Q LDOW| … p ⇒ … x | load word at p plus immediate offset in x
|
||||
R MALL| … x → … p | allocate memory for x bytes at p
|
||||
S ITOS| … x → … x | truncate x to 16 bits, then sign extend
|
||||
T PUSH| … x . y z ⇒ … y x ‥ z | push top of stack down by immediate places (opposite of ROLL)
|
||||
U CMPU| … x y → … z | compare x to y unsigned and set z such that x o y is z o 0
|
||||
V B_OR| … x y → … z | bitwise OR x and y and store in z
|
||||
W ITOW| … x → … x | truncate x to 32 bits, then sign extend
|
||||
X PUTX| … x → … | store x in rX
|
||||
Y PUTY| … x → … | store x in rY
|
||||
Z PUTZ| … x → … | store x in rZ
|
||||
a GETA| … → … x | load x from rA
|
||||
b GETB| … → … x | load x from rB
|
||||
c GETC| … → … x | load x from rC
|
||||
d MSET| … p x ⇒ … | set x bytes of memory to immediate value at p
|
||||
e SROT| … x y z → … y z x | rotate stack
|
||||
f FMOD| … a b → … c | set c to the modulo of a by b
|
||||
g FINV| … f → f … | invoke f, saving return address on stack
|
||||
h OVER| … x y → … x y x | stack over
|
||||
i GETI| … → … x | load x from rI
|
||||
j FNEG| … a → … a | negate a
|
||||
k SPOP| … x → … | stack pop
|
||||
l BITC| … x y → … z | bit clear y from x to z (z = x and not y)
|
||||
m FSUB| … a b → … c | set c to the difference between a and b (a - b)
|
||||
n GETN| … → … x | load x from rN
|
||||
o LDOB| … p ⇒ … x | load byte at p plus immediate offset in x
|
||||
p FMUL| … a b → … c | set c to the product of a and b
|
||||
q FDIV| … a b → … c | set c to the quotient of a by b
|
||||
r FREM| … a b → … c | set c to the remainder of a by b
|
||||
s FADD| … a b → … c | set c to the sum of a and b
|
||||
t SWAP| … x y → … y x | stack swap
|
||||
u USHR| … x ⇒ … x | logical shift x by immediate bits right
|
||||
v FABS| … a → … a | take the absolute value of a
|
||||
w SDUP| … x → … x x | stack duplicate
|
||||
x GETX| … → … x | load x from rX
|
||||
y GETY| … → … x | load x from rY
|
||||
z GETZ| … → … x | load x from rZ
|
||||
_ INEG| … x → … x | negate x
|
||||
. ITOF| … x → … a | convert x to a floating point number
|
||||
\+ IADD|… x y → … z | set z to the sum of x and y
|
||||
\- ISUB|… x y → … z | set z to the difference between x and y (x - y)
|
||||
\* IMUL|… x y → … z | set z to the product of x and y
|
||||
/ IDIV| … x y → … z | set z to the quotient of x by y
|
||||
% IMOD| … x y → … z | set z to the modulo of x by y
|
||||
\ IREM| … x y → … z | set z to the remainder of x by y
|
||||
\| IABS|… x → … x | take the absolute value of x
|
||||
$ VALU| … ↔ … * | load constant value
|
||||
@ CALL| … ↔ … | call immediate name
|
||||
< BLTZ| … x ⇒ … | branch to immediate if x less than zero
|
||||
{ BLEZ| … x ⇒ … | branch to immediate if x is less than or equal to zero
|
||||
\= BEQZ|… x ⇒ … | branch to immediate if x is zero
|
||||
} BGEZ| … x ⇒ … | branch to immediate if x is greater or equal to zero
|
||||
\> BGTZ|… x ⇒ … | branch to immediate if x is greater than zero
|
||||
, JUMP| … ⇒ … | jump to immediate without pushing return address
|
||||
; RETN| … f ⇒ … | return from subroutine (jump but for stack)
|
||||
& BAND| … x y → … z | set z to the bitwise and of x and y
|
||||
^ BXOR| … x y → … z | set z to the bitwise xor of x and y
|
||||
! BNOT| … x → y | set y to the bitwise not of x
|
||||
[ BSHL| … x ⇒ … x | left shift x by immediate bits
|
||||
] BSHR| … x ⇒ … x | arithmetic right shift x by immediate bits
|
||||
? CMPI| … x y → … z | compare x to y and set z such that x o y is z o 0
|
||||
~ CMPF| … a b ⇒ … c | compare a to b and set c such that a o b is z o c within an immediate error
|
||||
\: LABL|… f ↔ … | label code location
|
||||
( DPTH| … → … x | set x to depth of stack (before x)
|
||||
) PACK| … p x → … | pack x elements of stack (before p) into array p
|
||||
' DATA| | Embed data
|
||||
" SECT| | Change section
|
||||
\` BKPT| … ↔ … | trigger breakpoint, or exit if not debugging
|
||||
␣ NOOP| … ↔ … | do nothing, maybe end identifier definition
|
||||
¶ BEAT| … ↔ … | mark a beat for relative branching
|
||||
⋄ |Code| … | Stack | … | Description
|
||||
--|---:|----------:|:-:|:----------------|:-------------------------------------
|
||||
0 |BNEZ| … x| ⇒ | … | branch to immediate if x not zero
|
||||
1 |LDAB| … p| → | … x | load byte x from p
|
||||
2 |LDAS| … p| → | … x | load short x from p
|
||||
3 |LDAI| … p| → | … x | load int x from p
|
||||
4 |LDAW| … p| → | … x | load word x from p
|
||||
5 |STAB| … p x| → | … | store byte x to p
|
||||
6 |STAS| … p x| → | … | store short x to p
|
||||
7 |STAI| … p x| → | … | store int x to p
|
||||
8 |STAW| … p x| → | … | store word x to p
|
||||
9 |CASS| … p x y| → | … z | compare and swap x and y at p, return success
|
||||
A |PUTA| … x| → | … | store x in rA
|
||||
B |PUTB| … x| → | … | store x in rB
|
||||
C |PUTC| … x| → | … | store x in rC
|
||||
D |MCLR| … p x| → | … | memclear x bytes from p
|
||||
E |FTOI| … a| ⇒ | … x | round a to integer and store in x
|
||||
F |BMIS| … x| ⇒ | … x | bit manipulation instructions
|
||||
G |LDOI| … p| ⇒ | … x | load int at p plus immediate offset in x
|
||||
H |LDOS| … p| ⇒ | … x | load short at p plus immediate offset in x
|
||||
I |PUTI| … x| → | … | store x in rI
|
||||
J |MOFF| … p x| → | … q | set q to memory pointer p shifted by x bytes
|
||||
K |MCPY| … p q x| → | … | copy x bytes of memory from p to q
|
||||
L |ROLL| … x y ‥ z| ⇒ | … y ‥ z x | roll stack by immediate places
|
||||
M |MALL| … p| ⇒ | … p | (re)allocate/free memory for immediate bytes at p
|
||||
N |PUTN| … x| → | … | store x in rN
|
||||
O |ITOC| … x| → | … x | truncate x to 8 bits, then sign extend
|
||||
P |PICK| … x ‥ z| ⇒ | … x ‥ z x | pick stack element at immediate place
|
||||
Q |LDOW| … p| ⇒ | … x | load word at p plus immediate offset in x
|
||||
R |REAL| … p x| → | … p | (re)allocate/free memory for x bytes at p
|
||||
S |ITOS| … x| → | … x | truncate x to 16 bits, then sign extend
|
||||
T |PUSH| … x ‥ y z| ⇒ | … y x ‥ z | push top of stack down by immediate places (opposite of ROLL)
|
||||
U |CMPU| … x y| → | … z | compare x to y unsigned and set z such that x o y is z o 0
|
||||
V |BIOR| … x y| → | … z | bitwise OR x and y and store in z
|
||||
W |ITOW| … x| → | … x | truncate x to 32 bits, then sign extend
|
||||
X |PUTX| … x| → | … | store x in rX
|
||||
Y |PUTY| … x| → | … | store x in rY
|
||||
Z |PUTZ| … x| → | … | store x in rZ
|
||||
a |GETA| …| → | … x | load x from rA
|
||||
b |GETB| …| → | … x | load x from rB
|
||||
c |GETC| …| → | … x | load x from rC
|
||||
d |MSET| … p x| ⇒ | … | set x bytes of memory to immediate value at p
|
||||
e |SROT| … x y z| → | … y z x | rotate stack
|
||||
f |FMOD| … a b| → | … c | set c to the modulo of a by b
|
||||
g |FINV| … f| → | f … | invoke f, saving return address on stack
|
||||
h |OVER| … x y| → | … x y x | stack over
|
||||
i |GETI| …| → | … x | load x from rI
|
||||
j |FNEG| … a| → | … a | negate a
|
||||
k |SPOP| … x| → | … | stack pop
|
||||
l |BITC| … x y| → | … z | bit clear y from x to z (z = x and not y)
|
||||
m |FSUB| … a b| → | … c | set c to the difference between a and b (a - b)
|
||||
n |GETN| …| → | … x | load x from rN
|
||||
o |LDOB| … p| ⇒ | … x | load byte at p plus immediate offset in x
|
||||
p |FMUL| … a b| → | … c | set c to the product of a and b
|
||||
q |FDIV| … a b| → | … c | set c to the quotient of a by b
|
||||
r |FREM| … a b| → | … c | set c to the remainder of a by b
|
||||
s |FADD| … a b| → | … c | set c to the sum of a and b
|
||||
t |SWAP| … x y| → | … y x | stack swap
|
||||
u |USHR| … x| ⇒ | … x | logical shift x by immediate bits right
|
||||
v |FABS| … a| → | … a | take the absolute value of a
|
||||
w |SDUP| … x| → | … x x | stack duplicate
|
||||
x |GETX| …| → | … x | load x from rX
|
||||
y |GETY| …| → | … x | load x from rY
|
||||
z |GETZ| …| → | … x | load x from rZ
|
||||
\_|INEG| … x| → | … x | negate x
|
||||
. |ITOF| … x| → | … a | convert x to a floating point number
|
||||
\+|IADD| … x y| → | … z | set z to the sum of x and y
|
||||
\-|ISUB| … x y| → | … z | set z to the difference between x and y (x - y)
|
||||
\*|IMUL| … x y| → | … z | set z to the product of x and y
|
||||
/ |IDIV| … x y| → | … z | set z to the quotient of x by y
|
||||
% |IMOD| … x y| → | … z | set z to the modulo of x by y
|
||||
\\|IREM| … x y| → | … z | set z to the remainder of x by y
|
||||
\||IABS| … x| → | … x | take the absolute value of x
|
||||
$ |VALU| …| ⇒ | … \* | load constant value
|
||||
@ |CALL| …| → | f … | call immediate name
|
||||
\<|BLTZ| … x| ⇒ | … | branch to immediate if x less than zero
|
||||
{ |BLEZ| … x| ⇒ | … | branch to immediate if x is less than or equal to zero
|
||||
\=|BEQZ| … x| ⇒ | … | branch to immediate if x is zero
|
||||
} |BGEZ| … x| ⇒ | … | branch to immediate if x is greater or equal to zero
|
||||
\>|BGTZ| … x| ⇒ | … | branch to immediate if x is greater than zero
|
||||
, |JUMP| …| ⇒ | … | jump to immediate without pushing return address
|
||||
; |RETN| … f| ⇒ | … | return from subroutine (jump but for stack)
|
||||
\&|BAND| … x y| → | … z | set z to the bitwise and of x and y
|
||||
^ |BXOR| … x y| → | … z | set z to the bitwise xor of x and y
|
||||
! |BNOT| … x| → | y | set y to the bitwise not of x
|
||||
[ |ROTR| … x| ⇒ | … x | rotate x by immediate bits
|
||||
] |BSHR| … x| ⇒ | … x | arithmetic right shift x by immediate bits
|
||||
? |CMPI| … x y| → | … z | compare x to y and set z such that x o y is z o 0
|
||||
~ |CMPF| … a b| ⇒ | … c | compare a to b and set c such that a o b is z o c within an immediate error
|
||||
\:|LABL| … | ↔ | … | label code location
|
||||
( |DPTH| … | → | … x | set x to depth of stack (before x)
|
||||
) |PACK| … p x| → | … p | pack x elements of stack (before p) into array p
|
||||
' |DATA| | | | Embed data
|
||||
" |SECT| | | | Change section
|
||||
\`|BIFC| … ? | ↔ | … ? | call builtin with given name/value
|
||||
␣ |NOOP| …| ↔ | … | do nothing, maybe end identifier definition
|
||||
¶ |BEAT| …| ↔ | … | mark a beat for relative branching
|
||||
[Operations in order][ops-in-order]
|
||||
|
|
13
samples/hello.ctc
Normal file
13
samples/hello.ctc
Normal file
|
@ -0,0 +1,13 @@
|
|||
#!/usr/bin/env arctic
|
||||
|
||||
@main
|
||||
|
||||
"with stdio"
|
||||
|
||||
"data"
|
||||
:message 'Hello, World!'
|
||||
|
||||
"code"
|
||||
:main T3 kk # main is called as: &argv[0] argc retaddr
|
||||
$message @putstrln $0;
|
||||
|
|
@ -1,29 +1,41 @@
|
|||
#!/usr/bin/env arctic
|
||||
|
||||
# TODO: consider .ctc format instead?
|
||||
|
||||
@main
|
||||
|
||||
"data" # read-only memory
|
||||
:value '10' # 1 int
|
||||
:array '[1, 2, 3]' # 3 int array
|
||||
:values 'i10w10s10b10f10' # 1 each of int/word/short/byte/double in LE order
|
||||
:hexval 'I10W10S10B10F10' # 1 each of int/word/short/byte/double in BE order
|
||||
:bytes 'xffffffx' # 3 hexadecimal bytes
|
||||
:string '"hello"' # 5 utf-8 encoded bytes
|
||||
:array 'i1i2i3' # 3 int array
|
||||
|
||||
"state" # initialized read-write memory
|
||||
:variable '10' # 1 int
|
||||
:buffer '[1, 2, 3]' # 3 int array
|
||||
:variable 'i10' # 1 int
|
||||
:buffer 'i1i2i3' # 3 int array
|
||||
|
||||
"memory" # uninitialized read-write memory
|
||||
:pool '{10}' # 10 byte pool
|
||||
|
||||
"constants" # ephemeral data
|
||||
:const '10' # 1 universal integer
|
||||
:const 'i10' # 1 universal integer
|
||||
|
||||
"macros" # ephemeral code
|
||||
:foo
|
||||
:foo '[
|
||||
# body of foo here
|
||||
]'
|
||||
|
||||
"code" # loaded code
|
||||
:bar
|
||||
# body of bar here
|
||||
:main
|
||||
|
||||
:_baz # private label, not "exported" to other "code" sections
|
||||
# body of baz here
|
||||
|
||||
"code" # more loaded code, but in here _baz not yet defined
|
||||
# in general, all labels beginning with _ are section-local
|
||||
|
||||
"extern libfoo"
|
||||
:foobar # loaded from either libfoo.a or libfoo.so
|
||||
|
|
10
src/arctic.c
10
src/arctic.c
|
@ -14,7 +14,7 @@ const char ARCTIC_CODE_PAGE[97] =
|
|||
/* ops with an immediate argument */
|
||||
#define FOR_IMMEDIATE_OPS(X) \
|
||||
X('0') X('G') X('H') X('L') X('M') X('P') X('Q') X('d') X('o') X('u') \
|
||||
X('<') X('{') X('=') X('}') X('>') X(',') X(';') X('[') X(']')
|
||||
X('@') X('<') X('{') X('=') X('}') X('>') X(',') X(';') X('[') X(']')
|
||||
|
||||
#define FOR_PLAIN_OPS(X) \
|
||||
X('1') X('2') X('3') X('4') X('5') X('6') X('7') X('8') X('9') X('A') \
|
||||
|
@ -81,9 +81,9 @@ enum ArcticErrorCode arctic_scan(struct ArcticScanner *scanner, char next) {
|
|||
return ARCTIC_OK;
|
||||
|
||||
case ':': /* label name */
|
||||
if (next == ' ') {
|
||||
if (next == ' ' || next == '\n' || next == '#') {
|
||||
scanner->label_callback(scanner->buf + 1, scanner->data);
|
||||
scanner->buf[0] = 0;
|
||||
scanner->buf[0] = (next == '#' ? '#' : 0);
|
||||
} else {
|
||||
chrncat(
|
||||
scanner->buf, next, ARCTIC_BUFSIZE,
|
||||
|
@ -105,9 +105,9 @@ enum ArcticErrorCode arctic_scan(struct ArcticScanner *scanner, char next) {
|
|||
return ARCTIC_OK;
|
||||
|
||||
FOR_IMMEDIATE_OPS(CASE) /* immediate ops */
|
||||
if (next == ' ') {
|
||||
if (next == ' ' || next == '\n' || next == '#') {
|
||||
scanner->op_callback(scanner->buf[0], scanner->buf + 1, scanner->data);
|
||||
scanner->buf[0] = 0;
|
||||
scanner->buf[0] = (next == '#' ? '#' : 0);
|
||||
} else {
|
||||
chrncat(
|
||||
scanner->buf, next, ARCTIC_BUFSIZE,
|
||||
|
|
609
src/arctic.nim
609
src/arctic.nim
|
@ -1,8 +1,9 @@
|
|||
import std/[critbits, strformat, strutils, strmisc, re]
|
||||
import std/[atomics, bitops, critbits, strformat, strutils, strmisc, math, re]
|
||||
import bio
|
||||
import itertools
|
||||
|
||||
type
|
||||
BitShiftType = range[0 .. 64]
|
||||
ArcticTypeKind* = enum
|
||||
BYTE, SHORT, INT, WORD, DOUBLE
|
||||
|
||||
|
@ -10,10 +11,14 @@ type
|
|||
b: int8
|
||||
s: int16
|
||||
i: int32
|
||||
w: int64
|
||||
w: int64 # TODO: swap i and w to match instruction naming -.-
|
||||
u: uint64
|
||||
d: float64
|
||||
p: pointer # only used for dynamically allocated memory
|
||||
f: ArcticSymbol # also used for pointers to section memory
|
||||
f: ArcticSymbol # also used for pointers
|
||||
# TODO: Eventually replace f with p/f and each is just a uint64
|
||||
# Then load all memory into one long seq instead of 1 seq per
|
||||
# section. Probably actually have +ve be data memory, and -ve be
|
||||
# code memory (with sections mapping to ranges)
|
||||
|
||||
ArcticVariableIndex* = enum
|
||||
VARIABLE_A, VARIABLE_B, VARIABLE_C,
|
||||
|
@ -60,6 +65,9 @@ type
|
|||
|
||||
ArcticBuiltin* = proc (state: var ArcticState): ArcticStepResult
|
||||
|
||||
var
|
||||
next_malloc_id: int = 1
|
||||
|
||||
const
|
||||
ImmediateOps: set[char] = {'0', 'E', 'F', 'G', 'H', 'L', 'M', 'P', 'Q', 'd', 'o', 'u', '<', '{', '=', '}', '>', ',', ';', '[', ']'}
|
||||
PlainOps : set[char] = { '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'I', 'J', 'K', 'N', 'O', 'R', 'S', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'p', 'q', 'r', 's', 't', 'v', 'w', 'x', 'y', 'z', '_', '.', '+', '-', '*', '/', '%', '\\', '|', '&', '^', '!', '?', '(', ')', '`' }
|
||||
|
@ -309,6 +317,30 @@ func load*(code: string): ArcticState =
|
|||
else:
|
||||
discard
|
||||
|
||||
proc `[]`(state: ArcticState, symbol: ArcticSymbol): uint8 =
|
||||
assert symbol.section in state.sections
|
||||
let section = state.sections[symbol.section]
|
||||
|
||||
if section.iscode:
|
||||
return section.code[symbol.index].code.uint8
|
||||
else:
|
||||
return section.data[symbol.index]
|
||||
|
||||
proc `[]=`(state: var ArcticState, symbol: ArcticSymbol, value: uint8) =
|
||||
if not (symbol.section in state.sections):
|
||||
state.sections[symbol.section] = ArcticSection(iscode: false, data: @[])
|
||||
while state.sections[symbol.section].len <= symbol.index:
|
||||
state.sections[symbol.section].data.add 0
|
||||
state.sections[symbol.section].data[symbol.index] = value
|
||||
else:
|
||||
let section = state.sections[symbol.section]
|
||||
|
||||
while state.sections[symbol.section].data.len <= symbol.index:
|
||||
state.sections[symbol.section].data.add 0
|
||||
state.sections[symbol.section].data[symbol.index] = value
|
||||
|
||||
proc `+`(left: ArcticSymbol, offset: int): ArcticSymbol = (left.section, left.index + offset)
|
||||
|
||||
proc step*(state: var ArcticState, builtins: CritBitTree[ArcticBuiltin]): ArcticStepResult =
|
||||
let codesec = state.sections[state.pc.section]
|
||||
|
||||
|
@ -319,14 +351,579 @@ proc step*(state: var ArcticState, builtins: CritBitTree[ArcticBuiltin]): Arctic
|
|||
return ERROR
|
||||
|
||||
let op = codesec.code[state.pc.index]
|
||||
state.pc.index.inc
|
||||
|
||||
case op.code:
|
||||
case '1': # LDAB
|
||||
let top = state.stack[^1]
|
||||
# Memory Ops -----------------------------------------------------------
|
||||
of '1': # LDAB
|
||||
let p = state.stack.pop.f
|
||||
state.stack.add ArcticType(w: state[p].int64)
|
||||
|
||||
of '2': # LDAS
|
||||
let
|
||||
p = state.stack.pop.f
|
||||
a = state[p].int64
|
||||
b = state[p+1].int64
|
||||
if state.pc.section == "CODE": # bigendian
|
||||
state.stack.add ArcticType(w: a or b shl 8)
|
||||
else:
|
||||
state.stack.add ArcticType(w: b or a shl 8)
|
||||
|
||||
of '3': # LDAI
|
||||
let
|
||||
p = state.stack.pop.f
|
||||
a = state[p].int64
|
||||
b = state[p+1].int64
|
||||
c = state[p+2].int64
|
||||
d = state[p+3].int64
|
||||
if state.pc.section == "CODE": # bigendian
|
||||
state.stack.add ArcticType(w: d or c shl 8 or b shl 16 or a shl 24)
|
||||
else:
|
||||
state.stack.add ArcticType(w: a or b shl 8 or c shl 16 or d shl 24)
|
||||
|
||||
of '4': # LDAW
|
||||
let
|
||||
p = state.stack.pop.f
|
||||
a = state[p].int64
|
||||
b = state[p+1].int64
|
||||
c = state[p+2].int64
|
||||
d = state[p+3].int64
|
||||
e = state[p+4].int64
|
||||
f = state[p+5].int64
|
||||
g = state[p+6].int64
|
||||
h = state[p+7].int64
|
||||
if state.pc.section == "CODE": # bigendian
|
||||
state.stack.add ArcticType(w: h or g shl 8 or f shl 16 or e shl 24 or d shl 32 or c shl 40 or b shl 48 or a shl 56)
|
||||
else:
|
||||
state.stack.add ArcticType(w: a or b shl 8 or c shl 16 or d shl 24 or e shl 32 or f shl 40 or g shl 48 or h shl 56)
|
||||
|
||||
of 'o': # LDOB
|
||||
assert op.immediate.kind == INTEGER
|
||||
let p = state.stack.pop.f
|
||||
state.stack.add ArcticType(w: state[p+op.immediate.i].int64)
|
||||
|
||||
of 'H': # LDOS
|
||||
assert op.immediate.kind == INTEGER
|
||||
let
|
||||
p = state.stack.pop.f + op.immediate.i
|
||||
a = state[p].int64
|
||||
b = state[p+1].int64
|
||||
if state.pc.section == "CODE": # bigendian
|
||||
state.stack.add ArcticType(w: a or b shl 8)
|
||||
else:
|
||||
state.stack.add ArcticType(w: b or a shl 8)
|
||||
|
||||
of 'G': # LDOI
|
||||
let
|
||||
p = state.stack.pop.f + op.immediate.i
|
||||
a = state[p].int64
|
||||
b = state[p+1].int64
|
||||
c = state[p+2].int64
|
||||
d = state[p+3].int64
|
||||
if state.pc.section == "CODE": # bigendian
|
||||
state.stack.add ArcticType(w: d or c shl 8 or b shl 16 or a shl 24)
|
||||
else:
|
||||
state.stack.add ArcticType(w: a or b shl 8 or c shl 16 or d shl 24)
|
||||
|
||||
of 'Q': # LDOW
|
||||
let
|
||||
p = state.stack.pop.f
|
||||
a = state[p].int64
|
||||
b = state[p+1].int64
|
||||
c = state[p+2].int64
|
||||
d = state[p+3].int64
|
||||
e = state[p+4].int64
|
||||
f = state[p+5].int64
|
||||
g = state[p+6].int64
|
||||
h = state[p+7].int64
|
||||
if state.pc.section == "CODE": # bigendian
|
||||
state.stack.add ArcticType(w: h or g shl 8 or f shl 16 or e shl 24 or d shl 32 or c shl 40 or b shl 48 or a shl 56)
|
||||
else:
|
||||
state.stack.add ArcticType(w: a or b shl 8 or c shl 16 or d shl 24 or e shl 32 or f shl 40 or g shl 48 or h shl 56)
|
||||
|
||||
of '5': # STAB
|
||||
let
|
||||
x = cast[uint8](state.stack.pop.b)
|
||||
p = state.stack.pop.f
|
||||
state[p] = x
|
||||
|
||||
of '6': # STAS
|
||||
let
|
||||
x = cast[uint16](state.stack.pop.s)
|
||||
p = state.stack.pop.f
|
||||
if state.pc.section == "CODE": # bigendian
|
||||
state[p] = uint8(x and 0xFF)
|
||||
state[p+1] = uint8(x shr 8)
|
||||
else:
|
||||
state[p] = uint8(x shr 8)
|
||||
state[p+1] = uint8(x and 0xFF)
|
||||
|
||||
of '7': # STAI
|
||||
let
|
||||
x = cast[uint32](state.stack.pop.i)
|
||||
p = state.stack.pop.f
|
||||
if state.pc.section == "CODE": # bigendian
|
||||
state[p] = uint8(x and 0xFF)
|
||||
state[p+1] = uint8((x shr 8) and 0xFF)
|
||||
state[p+2] = uint8((x shr 16) and 0xFF)
|
||||
state[p+3] = uint8((x shr 24) and 0xFF)
|
||||
else:
|
||||
state[p] = uint8((x shr 24) and 0xFF)
|
||||
state[p+1] = uint8((x shr 16) and 0xFF)
|
||||
state[p+2] = uint8((x shr 8) and 0xFF)
|
||||
state[p+3] = uint8(x and 0xFF)
|
||||
|
||||
of '8': # STAW
|
||||
let
|
||||
x = cast[uint64](state.stack.pop.w)
|
||||
p = state.stack.pop.f
|
||||
if state.pc.section == "CODE": # bigendian
|
||||
state[p] = uint8(x and 0xFF)
|
||||
state[p+1] = uint8((x shr 8) and 0xFF)
|
||||
state[p+2] = uint8((x shr 16) and 0xFF)
|
||||
state[p+3] = uint8((x shr 24) and 0xFF)
|
||||
state[p+4] = uint8((x shr 32) and 0xFF)
|
||||
state[p+5] = uint8((x shr 40) and 0xFF)
|
||||
state[p+6] = uint8((x shr 48) and 0xFF)
|
||||
state[p+7] = uint8((x shr 56) and 0xFF)
|
||||
else:
|
||||
state[p] = uint8((x shr 56) and 0xFF)
|
||||
state[p+1] = uint8((x shr 48) and 0xFF)
|
||||
state[p+2] = uint8((x shr 40) and 0xFF)
|
||||
state[p+3] = uint8((x shr 32) and 0xFF)
|
||||
state[p+4] = uint8((x shr 24) and 0xFF)
|
||||
state[p+5] = uint8((x shr 16) and 0xFF)
|
||||
state[p+6] = uint8((x shr 8) and 0xFF)
|
||||
state[p+7] = uint8(x and 0xFF)
|
||||
|
||||
of 'D': # MCLR
|
||||
let
|
||||
x = state.stack.pop.w
|
||||
p = state.stack.pop.f
|
||||
for i in 0 .. x:
|
||||
state[p+i] = 0
|
||||
|
||||
of 'd': # MSET
|
||||
assert op.immediate.kind == INTEGER
|
||||
let
|
||||
x = state.stack.pop.w
|
||||
p = state.stack.pop.f
|
||||
b = op.immediate.i.uint8
|
||||
for i in 0 .. x:
|
||||
state[p+i] = b
|
||||
|
||||
of 'K': # MCPY
|
||||
let
|
||||
x = state.stack.pop.w
|
||||
q = state.stack.pop.f
|
||||
p = state.stack.pop.f
|
||||
for i in 0 .. x:
|
||||
state[q+i] = state[p+i]
|
||||
|
||||
of 'J': # MOFF
|
||||
let
|
||||
x = state.stack.pop.w
|
||||
p = state.stack.pop.f
|
||||
state.stack.add ArcticType(f: (section: p.section, index: p.index + 1))
|
||||
|
||||
of 'M': # MALL
|
||||
assert op.immediate.kind == INTEGER
|
||||
let
|
||||
top = state.stack.pop
|
||||
n = op.immediate.i
|
||||
|
||||
if n == 0:
|
||||
if top.w != 0:
|
||||
state.sections.excl top.f.section
|
||||
state.stack.add ArcticType(w: 0)
|
||||
elif n > 0:
|
||||
if top.w == 0:
|
||||
state.sections.incl("allocated " & $next_malloc_id, ArcticSection(iscode: false, data: @[]))
|
||||
inc next_malloc_id
|
||||
else:
|
||||
discard # we use dynamic memory, realloc is a no-op
|
||||
else:
|
||||
discard # realloc negative is a nop
|
||||
|
||||
|
||||
of 'R': # REAL
|
||||
let
|
||||
top = state.stack.pop
|
||||
x = state.stack.pop.i
|
||||
|
||||
if x == 0:
|
||||
if top.w != 0:
|
||||
state.sections.excl top.f.section
|
||||
state.stack.add ArcticType(w: 0)
|
||||
elif x > 0:
|
||||
if top.w == 0:
|
||||
state.sections.incl("allocated " & $next_malloc_id, ArcticSection(iscode: false, data: @[]))
|
||||
inc next_malloc_id
|
||||
else:
|
||||
discard # we use dynamic memory, realloc is a no-op
|
||||
else:
|
||||
discard # realloc negative is a nop
|
||||
|
||||
|
||||
of '9': # CASS
|
||||
# TODO: Actually CAS? need to use Atomic[T] for that...
|
||||
# implementation would probably require an "atomic" memory
|
||||
# section
|
||||
let
|
||||
y = state.stack.pop.w
|
||||
x = state.stack.pop.w
|
||||
p = state.stack.pop.f
|
||||
a = state[p].int64
|
||||
b = state[p+1].int64
|
||||
c = state[p+2].int64
|
||||
d = state[p+3].int64
|
||||
e = state[p+4].int64
|
||||
f = state[p+5].int64
|
||||
g = state[p+6].int64
|
||||
h = state[p+7].int64
|
||||
|
||||
var old: int64
|
||||
if state.pc.section == "CODE": # bigendian
|
||||
old = h or g shl 8 or f shl 16 or e shl 24 or d shl 32 or c shl 40 or b shl 48 or a shl 56
|
||||
else:
|
||||
old = a or b shl 8 or c shl 16 or d shl 24 or e shl 32 or f shl 40 or g shl 48 or h shl 56
|
||||
|
||||
if x == old:
|
||||
state.stack.add ArcticType(i: 1)
|
||||
if state.pc.section == "CODE": # bigendian
|
||||
state[p] = uint8(y and 0xFF)
|
||||
state[p+1] = uint8((y shr 8) and 0xFF)
|
||||
state[p+2] = uint8((y shr 16) and 0xFF)
|
||||
state[p+3] = uint8((y shr 24) and 0xFF)
|
||||
state[p+4] = uint8((y shr 32) and 0xFF)
|
||||
state[p+5] = uint8((y shr 40) and 0xFF)
|
||||
state[p+6] = uint8((y shr 48) and 0xFF)
|
||||
state[p+7] = uint8((y shr 56) and 0xFF)
|
||||
else:
|
||||
state[p] = uint8((y shr 56) and 0xFF)
|
||||
state[p+1] = uint8((y shr 48) and 0xFF)
|
||||
state[p+2] = uint8((y shr 40) and 0xFF)
|
||||
state[p+3] = uint8((y shr 32) and 0xFF)
|
||||
state[p+4] = uint8((y shr 24) and 0xFF)
|
||||
state[p+5] = uint8((y shr 16) and 0xFF)
|
||||
state[p+6] = uint8((y shr 8) and 0xFF)
|
||||
state[p+7] = uint8(y and 0xFF)
|
||||
else:
|
||||
state.stack.add ArcticType(w: 0)
|
||||
|
||||
|
||||
# Math Ops -----------------------------------------------------------
|
||||
of '+': # IADD
|
||||
let
|
||||
y = state.stack.pop.w
|
||||
x = state.stack.pop.w
|
||||
|
||||
state.stack.add ArcticType(w: x + y)
|
||||
|
||||
of '-': # ISUB
|
||||
let
|
||||
y = state.stack.pop.w
|
||||
x = state.stack.pop.w
|
||||
|
||||
state.stack.add ArcticType(w: x - y)
|
||||
|
||||
of '*': # IMUL
|
||||
let
|
||||
y = state.stack.pop.w
|
||||
x = state.stack.pop.w
|
||||
|
||||
state.stack.add ArcticType(w: x * y)
|
||||
|
||||
of '/': # IDIV
|
||||
let
|
||||
y = state.stack.pop.w
|
||||
x = state.stack.pop.w
|
||||
|
||||
state.stack.add ArcticType(w: int(x / y))
|
||||
|
||||
of '%': # IMOD
|
||||
let
|
||||
y = state.stack.pop.w
|
||||
x = state.stack.pop.w
|
||||
|
||||
state.stack.add ArcticType(w: x.floorMod(y))
|
||||
|
||||
of '\\': # IREM
|
||||
let
|
||||
y = state.stack.pop.w
|
||||
x = state.stack.pop.w
|
||||
|
||||
state.stack.add ArcticType(w: x mod y)
|
||||
|
||||
of '_': # INEG
|
||||
let x = state.stack.pop.w
|
||||
state.stack.add ArcticType(w: -x)
|
||||
|
||||
of '|': # IABS
|
||||
let x = state.stack.pop.w
|
||||
state.stack.add ArcticType(w: x.abs)
|
||||
|
||||
of 's': # FADD
|
||||
let
|
||||
b = state.stack.pop.d
|
||||
a = state.stack.pop.d
|
||||
|
||||
state.stack.add ArcticType(d: a + b)
|
||||
|
||||
of 'm': # FSUB
|
||||
let
|
||||
b = state.stack.pop.d
|
||||
a = state.stack.pop.d
|
||||
|
||||
state.stack.add ArcticType(d: a - b)
|
||||
|
||||
of 'p': # FMUL
|
||||
let
|
||||
b = state.stack.pop.d
|
||||
a = state.stack.pop.d
|
||||
|
||||
state.stack.add ArcticType(d: a * b)
|
||||
|
||||
of 'q': # FDIV
|
||||
let
|
||||
b = state.stack.pop.d
|
||||
a = state.stack.pop.d
|
||||
|
||||
state.stack.add ArcticType(d: a / b)
|
||||
|
||||
of 'f': # FMOD
|
||||
let
|
||||
b = state.stack.pop.d
|
||||
a = state.stack.pop.d
|
||||
|
||||
state.stack.add ArcticType(d: floorMod(a, b))
|
||||
|
||||
of 'r': # FREM
|
||||
let
|
||||
b = state.stack.pop.d
|
||||
a = state.stack.pop.d
|
||||
|
||||
state.stack.add ArcticType(d: a mod b)
|
||||
|
||||
of 'j': # FNEG
|
||||
let
|
||||
a = state.stack.pop.d
|
||||
|
||||
state.stack.add ArcticType(d: -a)
|
||||
|
||||
of 'v': # FABS
|
||||
let
|
||||
a = state.stack.pop.d
|
||||
|
||||
state.stack.add ArcticType(d: a.abs)
|
||||
|
||||
of 'V': # BIOR
|
||||
let
|
||||
y = state.stack.pop.w
|
||||
x = state.stack.pop.w
|
||||
|
||||
state.stack.add ArcticType(w: x or y)
|
||||
|
||||
of '&': # BAND
|
||||
let
|
||||
y = state.stack.pop.w
|
||||
x = state.stack.pop.w
|
||||
|
||||
state.stack.add ArcticType(w: x and y)
|
||||
|
||||
of '^': # BXOR
|
||||
let
|
||||
y = state.stack.pop.w
|
||||
x = state.stack.pop.w
|
||||
|
||||
state.stack.add ArcticType(w: x xor y)
|
||||
|
||||
of 'l': # BITC
|
||||
let
|
||||
y = state.stack.pop.w
|
||||
x = state.stack.pop.w
|
||||
|
||||
state.stack.add ArcticType(w: x and (not y))
|
||||
|
||||
of '!': # BNOT
|
||||
let x = state.stack.pop.w
|
||||
|
||||
state.stack.add ArcticType(w: not x)
|
||||
|
||||
of 'u': # USHR
|
||||
let x = state.stack.pop.w
|
||||
var i: int64
|
||||
case op.immediate.kind:
|
||||
of INTEGER:
|
||||
i = op.immediate.i
|
||||
of VARIABLE:
|
||||
let v = op.immediate.v
|
||||
i = state.registers[v].w
|
||||
else:
|
||||
assert false
|
||||
|
||||
if i > 0:
|
||||
state.stack.add ArcticType(w: x shr i)
|
||||
else:
|
||||
state.stack.add ArcticType(w: x shl (-i))
|
||||
|
||||
of '[': # ROTR
|
||||
let x = state.stack.pop.u
|
||||
var i: int64
|
||||
case op.immediate.kind:
|
||||
of INTEGER:
|
||||
i = op.immediate.i
|
||||
of VARIABLE:
|
||||
let v = op.immediate.v
|
||||
i = state.registers[v].w
|
||||
else:
|
||||
assert false
|
||||
|
||||
if i > 0:
|
||||
state.stack.add ArcticType(u: rotateRightBits(x, i))
|
||||
else:
|
||||
state.stack.add ArcticType(u: rotateLeftBits(x, -i))
|
||||
|
||||
of ']': # BSHR
|
||||
let x = state.stack.pop.u
|
||||
var i: int64
|
||||
case op.immediate.kind:
|
||||
of INTEGER:
|
||||
i = op.immediate.i
|
||||
of VARIABLE:
|
||||
let v = op.immediate.v
|
||||
i = state.registers[v].w
|
||||
else:
|
||||
assert false
|
||||
|
||||
if i > 0:
|
||||
state.stack.add ArcticType(u: rotateRightBits(x, i))
|
||||
else:
|
||||
state.stack.add ArcticType(u: rotateLeftBits(x, -i))
|
||||
|
||||
of '?': # CMPI
|
||||
let
|
||||
y = state.stack.pop.w
|
||||
x = state.stack.pop.w
|
||||
if x > y:
|
||||
state.stack.add ArcticType(w: 1)
|
||||
elif y > x:
|
||||
state.stack.add ArcticType(w: -1)
|
||||
else:
|
||||
state.stack.add ArcticType(w: 0)
|
||||
|
||||
of '~': # CMPF
|
||||
assert op.immediate.kind == INTEGER
|
||||
let
|
||||
b = state.stack.pop.d
|
||||
a = state.stack.pop.d
|
||||
if almostEqual(a, b, op.immediate.i.Natural):
|
||||
state.stack.add ArcticType(d: 0.0)
|
||||
elif a > b:
|
||||
state.stack.add ArcticType(d: 1.0)
|
||||
else:
|
||||
state.stack.add ArcticType(d: -1.0)
|
||||
|
||||
of 'U': # CMPU
|
||||
let
|
||||
y = state.stack.pop.u
|
||||
x = state.stack.pop.u
|
||||
if x > y:
|
||||
state.stack.add ArcticType(w: 1)
|
||||
elif y > x:
|
||||
state.stack.add ArcticType(w: -1)
|
||||
else:
|
||||
state.stack.add ArcticType(w: 0)
|
||||
|
||||
of 'F': # BMIS
|
||||
discard # TODO: define BMIS
|
||||
|
||||
# Type Conversion Ops --------------------------------------------------
|
||||
of 'E': # FTOI
|
||||
let a = state.stack.pop.d
|
||||
state.stack.add ArcticType(w: a.int64)
|
||||
|
||||
of 'O': # ITOC
|
||||
let x = state.stack.pop.b
|
||||
state.stack.add ArcticType(w: x.int64)
|
||||
|
||||
of 'S': # ITOS
|
||||
let x = state.stack.pop.s
|
||||
state.stack.add ArcticType(w: x.int64)
|
||||
|
||||
of 'W': # ITOW
|
||||
let x = state.stack.pop.i
|
||||
state.stack.add ArcticType(w: x.int64)
|
||||
|
||||
of '.': # ITOF
|
||||
let x = state.stack.pop.w
|
||||
state.stack.add ArcticType(d: x.float64)
|
||||
|
||||
# Stack Ops ------------------------------------------------------------
|
||||
of 'w': # SDUP
|
||||
let x = state.stack.pop
|
||||
state.stack.add x
|
||||
state.stack.add x
|
||||
|
||||
of 'k': # SPOP
|
||||
discard state.stack.pop
|
||||
|
||||
of 't': # SWAP
|
||||
let
|
||||
y = state.stack.pop
|
||||
x = state.stack.pop
|
||||
state.stack.add y
|
||||
state.stack.add x
|
||||
|
||||
of 'h': # OVER
|
||||
let
|
||||
y = state.stack.pop
|
||||
x = state.stack.pop
|
||||
state.stack.add x
|
||||
state.stack.add y
|
||||
state.stack.add x
|
||||
|
||||
of 'e': # SROT
|
||||
let
|
||||
z = state.stack.pop
|
||||
y = state.stack.pop
|
||||
x = state.stack.pop
|
||||
state.stack.add y
|
||||
state.stack.add z
|
||||
state.stack.add x
|
||||
|
||||
of 'P': # PICK
|
||||
assert op.immediate.kind == INTEGER
|
||||
let val = state.stack[^op.immediate.i]
|
||||
state.stack.add val
|
||||
|
||||
of 'L': # ROLL
|
||||
assert op.immediate.kind == INTEGER
|
||||
let idx: int = state.stack.len - op.immediate.i # TODO: check for OBOEs
|
||||
state.stack.delete(idx)
|
||||
|
||||
of 'T': # PUSH
|
||||
assert op.immediate.kind == INTEGER
|
||||
let
|
||||
idx: int = state.stack.len - op.immediate.i # TODO: check for OBOEs
|
||||
x = state.stack.pop
|
||||
state.stack.insert(x, idx)
|
||||
|
||||
of '(': # DPTH
|
||||
state.stack.add ArcticType(w: state.stack.len)
|
||||
|
||||
of ')': # PACK
|
||||
let
|
||||
x = state.stack.pop.w
|
||||
p = state.stack.pop.f
|
||||
# TODO: Can't implement this until we have unified addresses
|
||||
# Once we have the addresses, can pack x items of stack at p+...
|
||||
|
||||
# Control Ops ----------------------------------------------------------
|
||||
## TODO
|
||||
|
||||
## TODO
|
||||
|
||||
|
||||
when isMainModule:
|
||||
let state = stdin.readAll.load
|
||||
|
|
Loading…
Reference in a new issue