Restructured interpreter

This commit is contained in:
Louis Burke 2023-12-25 20:18:08 -05:00
parent c990114723
commit 2836e25e65
8 changed files with 1093 additions and 1044 deletions

4
.gitignore vendored
View file

@ -1,3 +1,3 @@
build/
arctic
/arctic
htmldocs

View file

@ -6,11 +6,14 @@ description = "ARCTIC in Nim"
license = "Apache-2.0"
srcDir = "src"
installExt = @["nim"]
bin = @["arctic"]
bin = @["arctic", "arctic/load"]
namedBin = {"arctic": "arctic", "arctic/load": "acdump"}.toTable()
# Dependencies
requires "nim >= 2.0.0"
requires "bio"
requires "itertools"
task docs, "docs":
exec "nim doc --project --index:on --outdir:src/htmldocs src/arctic.nim"

File diff suppressed because it is too large Load diff

3
src/arctic/constants.nim Normal file
View file

@ -0,0 +1,3 @@
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', '_', '.', '+', '-', '*', '/', '%', '\\', '|', '&', '^', '!', '?', '(', ')' }

197
src/arctic/load.nim Normal file
View file

@ -0,0 +1,197 @@
import std/[critbits, re, strutils, strmisc]
import bio
import itertools
import constants
import memory
import types
proc tovar(code: char): ArcticVariableIndex =
case code.toLowerAscii:
of 'a': VARIABLE_A
of 'b': VARIABLE_B
of 'c': VARIABLE_C
of 'i': VARIABLE_I
of 'n': VARIABLE_N
of 'x': VARIABLE_X
of 'y': VARIABLE_Y
of 'z': VARIABLE_Z
else: raise new ValueError
proc parse_immediate(imm: string, section: string): ArcticImmediate =
if imm.match(re"^\d+$"):
return ArcticImmediate(kind: INTEGER, i: imm.parseInt)
elif imm.match(re"^[AaBbCcIiNnXxYyZz]$"):
return ArcticImmediate(kind: VARIABLE, v: imm[0].tovar)
elif imm.len > 0 and imm[0] in "0123456789.":
return ArcticImmediate(kind: NUMBER, n: imm.parseFloat)
else:
if imm.len > 0 and imm[0] == '_':
return ArcticImmediate(kind: SYMBOL, s: section & " " & imm)
else:
return ArcticImmediate(kind: SYMBOL, s: imm)
func grabnum(data: string): (string, string) =
## Returns the numeric literal at the start of data, then the rest of data
var i = 0
while i < data.len and data[i] in "0123456789.":
result[0] &= data[i]
i += 1
result[1] = data[i..^1]
proc parse_data(data: string): seq[uint8] =
if data.len == 0:
return
template insertInt(itype, utype, endianness) =
let (text, rest) = data[1..^1].grabnum
if text[0] == '-':
let val: itype = itype(-text[1..^1].parseBiggestInt)
result.add cast[utype](val).serialize(endianness)
else:
let val: utype = utype(text.parseBiggestUInt)
result.add val.serialize(endianness)
result.add parse_data(rest)
case data[0]:
of 'i': insertInt(int64, uint64, littleEndian)
of 'I': insertInt(int64, uint64, bigEndian)
of 'w': insertInt(int32, uint32, littleEndian)
of 'W': insertInt(int32, uint32, bigEndian)
of 's': insertInt(int16, uint16, littleEndian)
of 'S': insertInt(int16, uint16, bigEndian)
of 'b', 'B': # bytes don't have an endianness
let (text, rest) = data[1..^1].grabnum
if text[0] == '-':
let val: int8 = int8(-text[1..^1].parseBiggestInt)
result.add cast[uint8](val)
else:
let val: uint8 = uint8(text.parseBiggestUInt)
result.add val
result.add parse_data(rest)
of 'f':
let (text, rest) = data[1..^1].grabnum
result.add cast[uint64](text.parseFloat).serialize(littleEndian)
result.add parse_data(rest)
of 'F':
let (text, rest) = data[1..^1].grabnum
result.add cast[uint64](text.parseFloat).serialize(bigEndian)
result.add parse_data(rest)
of 'x': # hexadecimal byte constant
let (hex, _, rest) = data[1..^1].partition("x")
for pair in hex.chunked(2):
result.add uint8(parseHexInt(pair.join))
result.add parse_data(rest)
of '"': # utf-8 string constant with escapes
let strlen = data.matchLen(re"^""([^""]|\\"")*""")
assert strlen >= 0
for c in unescape(data[1..strlen - 2], prefix="", suffix=""):
result.add c.uint8
result.add parse_data(data[strlen..^1])
else:
result.add parse_data(data[1..^1])
func iscode(secname: string): bool =
if secname.len == 0:
return true
return secname[0] in "cC"
func isbigendian(secname: string): bool =
if secname.len == 0:
return false
return secname[0] in "C"
func raw_op(code: char): ArcticOperation =
result.code = code
result.immediate = ArcticImmediate(kind: PLAIN)
func load*(code: string): ArcticState =
var
section: string = ""
token: string = ""
result.pc = 1
for next in code:
if token.len == 0: # initial state
case next:
of ImmediateOps, '#', ':', '\"', '\'':
token &= next
of PlainOps, '\n':
result.code.add(section, raw_op(next))
else:
discard
else:
case token[0]:
of '#': # comment
if next == '\n':
token = ""
result.code.add(section, raw_op(next))
of '"': # section switch
if next == '"' and token[^1] != '\\':
section = token[1..^1]
token = ""
else:
token.add next
of ':': # label name
if next == ' ' or next == '\n':
let idx = if token[1] == '_': section & " " & token[1..^1] else: token[1..^1]
if section.iscode:
result.symbols[idx] = result.code.current(section)
else:
result.symbols[idx] = result.code.current(section)
token = ""
else:
token.add next
of '\'': # data injection
if next == '\'':
var
quoted = false
escaped = false
for c in token:
if c == '"':
if not escaped:
quoted = not quoted
escaped = false
elif c == '\\':
escaped = true
else:
escaped = false
if quoted or escaped:
token.add next
else:
result.memory.add(section, parse_data(token[1..^1]))
token = ""
else:
token.add next
of ImmediateOps:
if next in " \n":
result.code.add(section, ArcticOperation(
code: token[0],
immediate: parse_immediate(token[1..^1], section),
bigendian: isbigendian(section)))
token = ""
else:
token.add next
else:
discard
when isMainModule:
let state = stdin.readAll.load
echo state

View file

@ -163,6 +163,14 @@ proc `[]=`*[T](memory: var Memory[T], address: int, value: T) =
let (blkid, offset) = divmod(address - HUG_BLOCK_START, HUG_BLOCK_SIZE)
memory.hugmem[blkid][offset] = value
proc write*[T](memory: var Memory[T], address: int, values: openArray[T]) =
## Writes to the underlying memory item for a given address, failing if it
## does not exist
var a = address
for i in values:
memory[a] = i
inc a
proc section*[T](memory: Memory[T], address: int): string =
## Calculates which section a given address points to (warning: slow)
let (idx, off) = memory.addrinfo(address)

742
src/arctic/step.nim Normal file
View file

@ -0,0 +1,742 @@
import std/[bitops, critbits, math]
import memory
import types
proc branch(state: var ArcticState, count: int) =
var n = 0
if count > 0:
while n < count:
state.pc.inc
if state.code[state.pc].code == '\n':
n.inc
else:
while n > count:
state.pc.dec
if state.code[state.pc].code == '\n':
n.dec
proc step*(state: var ArcticState, builtins: CritBitTree[ArcticBuiltin]): ArcticStepResult =
if not (state.pc in state.code):
return EXIT
let op = state.code[state.pc]
state.pc.inc
case op.code:
# Memory Ops -----------------------------------------------------------
of '1': # LDAB
let p = state.stack.pop.p
state.stack.add ArcticType(i: state.memory[p].int64)
of '2': # LDAS
let
p = state.stack.pop.p
a = state.memory[p].int64
b = state.memory[p+1].int64
if op.bigendian:
state.stack.add ArcticType(i: a or b shl 8)
else:
state.stack.add ArcticType(i: b or a shl 8)
of '3': # LDAI
let
p = state.stack.pop.p
a = state.memory[p].int64
b = state.memory[p+1].int64
c = state.memory[p+2].int64
d = state.memory[p+3].int64
if op.bigendian:
state.stack.add ArcticType(i: d or c shl 8 or b shl 16 or a shl 24)
else:
state.stack.add ArcticType(i: a or b shl 8 or c shl 16 or d shl 24)
of '4': # LDAW
let
p = state.stack.pop.p
a = state.memory[p].int64
b = state.memory[p+1].int64
c = state.memory[p+2].int64
d = state.memory[p+3].int64
e = state.memory[p+4].int64
f = state.memory[p+5].int64
g = state.memory[p+6].int64
h = state.memory[p+7].int64
if op.bigendian:
state.stack.add ArcticType(i: 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(i: 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.p
state.stack.add ArcticType(i: state.memory[p+op.immediate.i].int64)
of 'H': # LDOS
assert op.immediate.kind == INTEGER
let
p = state.stack.pop.p + op.immediate.i
a = state.memory[p].int64
b = state.memory[p+1].int64
if op.bigendian:
state.stack.add ArcticType(i: a or b shl 8)
else:
state.stack.add ArcticType(i: b or a shl 8)
of 'G': # LDOI
let
p = state.stack.pop.p + op.immediate.i
a = state.memory[p].int64
b = state.memory[p+1].int64
c = state.memory[p+2].int64
d = state.memory[p+3].int64
if op.bigendian:
state.stack.add ArcticType(i: d or c shl 8 or b shl 16 or a shl 24)
else:
state.stack.add ArcticType(i: a or b shl 8 or c shl 16 or d shl 24)
of 'Q': # LDOW
let
p = state.stack.pop.p
a = state.memory[p].int64
b = state.memory[p+1].int64
c = state.memory[p+2].int64
d = state.memory[p+3].int64
e = state.memory[p+4].int64
f = state.memory[p+5].int64
g = state.memory[p+6].int64
h = state.memory[p+7].int64
if op.bigendian:
state.stack.add ArcticType(i: 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(i: 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.p
state.memory[p] = x
of '6': # STAS
let
x = cast[uint16](state.stack.pop.s)
p = state.stack.pop.p
if op.bigendian:
state.memory[p] = uint8(x and 0xFF)
state.memory[p+1] = uint8(x shr 8)
else:
state.memory[p] = uint8(x shr 8)
state.memory[p+1] = uint8(x and 0xFF)
of '7': # STAI
let
x = cast[uint32](state.stack.pop.i)
p = state.stack.pop.p
if op.bigendian:
state.memory[p] = uint8(x and 0xFF)
state.memory[p+1] = uint8((x shr 8) and 0xFF)
state.memory[p+2] = uint8((x shr 16) and 0xFF)
state.memory[p+3] = uint8((x shr 24) and 0xFF)
else:
state.memory[p] = uint8((x shr 24) and 0xFF)
state.memory[p+1] = uint8((x shr 16) and 0xFF)
state.memory[p+2] = uint8((x shr 8) and 0xFF)
state.memory[p+3] = uint8(x and 0xFF)
of '8': # STAW
let
x = cast[uint64](state.stack.pop.i)
p = state.stack.pop.p
if op.bigendian:
state.memory[p] = uint8(x and 0xFF)
state.memory[p+1] = uint8((x shr 8) and 0xFF)
state.memory[p+2] = uint8((x shr 16) and 0xFF)
state.memory[p+3] = uint8((x shr 24) and 0xFF)
state.memory[p+4] = uint8((x shr 32) and 0xFF)
state.memory[p+5] = uint8((x shr 40) and 0xFF)
state.memory[p+6] = uint8((x shr 48) and 0xFF)
state.memory[p+7] = uint8((x shr 56) and 0xFF)
else:
state.memory[p] = uint8((x shr 56) and 0xFF)
state.memory[p+1] = uint8((x shr 48) and 0xFF)
state.memory[p+2] = uint8((x shr 40) and 0xFF)
state.memory[p+3] = uint8((x shr 32) and 0xFF)
state.memory[p+4] = uint8((x shr 24) and 0xFF)
state.memory[p+5] = uint8((x shr 16) and 0xFF)
state.memory[p+6] = uint8((x shr 8) and 0xFF)
state.memory[p+7] = uint8(x and 0xFF)
of 'D': # MCLR
let
x = state.stack.pop.i
p = state.stack.pop.p
for i in 0 .. x:
state.memory[p+i] = 0
of 'd': # MSET
assert op.immediate.kind == INTEGER
let
x = state.stack.pop.i
p = state.stack.pop.p
b = op.immediate.i.uint8
for i in 0 .. x:
state.memory[p+i] = b
of 'K': # MCPY
let
x = state.stack.pop.i
q = state.stack.pop.p
p = state.stack.pop.p
for i in 0 .. x:
state.memory[q+i] = state.memory[p+i]
of 'J': # MOFF
let
x = state.stack.pop.i
p = state.stack.pop.p
state.stack.add ArcticType(p: p + x)
of 'M': # MALL
assert op.immediate.kind == INTEGER
let
top = state.stack.pop
n = op.immediate.i
if n == 0:
if top.i != 0:
state.memory.deallocate(top.p)
state.stack.add ArcticType(i: 0)
elif n > 0:
if top.i == 0:
state.stack.add ArcticType(p: state.memory.allocate(n))
else:
state.stack.add ArcticType(p: state.memory.reallocate(top.p, n))
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.i != 0:
state.memory.deallocate(top.p)
state.stack.add ArcticType(i: 0)
elif x > 0:
if top.i == 0:
state.stack.add ArcticType(p: state.memory.allocate(x))
else:
state.stack.add ArcticType(p: state.memory.reallocate(top.p, x))
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.i
x = state.stack.pop.i
p = state.stack.pop.p
a = state.memory[p].int64
b = state.memory[p+1].int64
c = state.memory[p+2].int64
d = state.memory[p+3].int64
e = state.memory[p+4].int64
f = state.memory[p+5].int64
g = state.memory[p+6].int64
h = state.memory[p+7].int64
var old: int64
if op.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 op.bigendian:
state.memory[p] = uint8(y and 0xFF)
state.memory[p+1] = uint8((y shr 8) and 0xFF)
state.memory[p+2] = uint8((y shr 16) and 0xFF)
state.memory[p+3] = uint8((y shr 24) and 0xFF)
state.memory[p+4] = uint8((y shr 32) and 0xFF)
state.memory[p+5] = uint8((y shr 40) and 0xFF)
state.memory[p+6] = uint8((y shr 48) and 0xFF)
state.memory[p+7] = uint8((y shr 56) and 0xFF)
else:
state.memory[p] = uint8((y shr 56) and 0xFF)
state.memory[p+1] = uint8((y shr 48) and 0xFF)
state.memory[p+2] = uint8((y shr 40) and 0xFF)
state.memory[p+3] = uint8((y shr 32) and 0xFF)
state.memory[p+4] = uint8((y shr 24) and 0xFF)
state.memory[p+5] = uint8((y shr 16) and 0xFF)
state.memory[p+6] = uint8((y shr 8) and 0xFF)
state.memory[p+7] = uint8(y and 0xFF)
else:
state.stack.add ArcticType(i: 0)
# Math Ops -----------------------------------------------------------
of '+': # IADD
let
y = state.stack.pop.i
x = state.stack.pop.i
state.stack.add ArcticType(i: x + y)
of '-': # ISUB
let
y = state.stack.pop.i
x = state.stack.pop.i
state.stack.add ArcticType(i: x - y)
of '*': # IMUL
let
y = state.stack.pop.i
x = state.stack.pop.i
state.stack.add ArcticType(i: x * y)
of '/': # IDIV
let
y = state.stack.pop.i
x = state.stack.pop.i
state.stack.add ArcticType(i: int(x / y))
of '%': # IMOD
let
y = state.stack.pop.i
x = state.stack.pop.i
state.stack.add ArcticType(i: x.floorMod(y))
of '\\': # IREM
let
y = state.stack.pop.i
x = state.stack.pop.i
state.stack.add ArcticType(i: x mod y)
of '_': # INEG
let x = state.stack.pop.i
state.stack.add ArcticType(i: -x)
of '|': # IABS
let x = state.stack.pop.i
state.stack.add ArcticType(i: 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.i
x = state.stack.pop.i
state.stack.add ArcticType(i: x or y)
of '&': # BAND
let
y = state.stack.pop.i
x = state.stack.pop.i
state.stack.add ArcticType(i: x and y)
of '^': # BXOR
let
y = state.stack.pop.i
x = state.stack.pop.i
state.stack.add ArcticType(i: x xor y)
of 'l': # BITC
let
y = state.stack.pop.i
x = state.stack.pop.i
state.stack.add ArcticType(i: x and (not y))
of '!': # BNOT
let x = state.stack.pop.i
state.stack.add ArcticType(i: not x)
of 'u': # USHR
let x = state.stack.pop.i
var i: int64
case op.immediate.kind:
of INTEGER:
i = op.immediate.i
of VARIABLE:
let v = op.immediate.v
i = state.registers[v].i
else:
echo "Unexpected immediate: ", op
assert false
if i > 0:
state.stack.add ArcticType(i: x shr i)
else:
state.stack.add ArcticType(i: 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].i
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].i
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.i
x = state.stack.pop.i
if x > y:
state.stack.add ArcticType(i: 1)
elif y > x:
state.stack.add ArcticType(i: -1)
else:
state.stack.add ArcticType(i: 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(i: 1)
elif y > x:
state.stack.add ArcticType(i: -1)
else:
state.stack.add ArcticType(i: 0)
of 'F': # BMIS
discard # TODO: define BMIS
# Type Conversion Ops --------------------------------------------------
of 'E': # FTOI
let a = state.stack.pop.d
state.stack.add ArcticType(i: a.int64)
of 'O': # ITOC
let x = state.stack.pop.b
state.stack.add ArcticType(i: x.int64)
of 'S': # ITOS
let x = state.stack.pop.s
state.stack.add ArcticType(i: x.int64)
of 'W': # ITOW
let x = state.stack.pop.i
state.stack.add ArcticType(i: x.int64)
of '.': # ITOF
let x = state.stack.pop.i
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(i: state.stack.len)
of ')': # PACK
let x = state.stack.pop.i
var p = state.stack.pop.p
for i in countdown(x - 1, 0):
var n = state.stack[^i].u
for _ in 0 .. 8:
state.memory[p] = uint8(n and 0xFF)
n = n shr 8
inc p
# Control Ops ----------------------------------------------------------
of '=': # BEQZ
assert op.immediate.kind == INTEGER
let cond = state.stack.pop.i
if cond == 0:
state.branch op.immediate.i
of '0': # BNEZ
assert op.immediate.kind == INTEGER
let cond = state.stack.pop.i
if cond != 0:
state.branch op.immediate.i
of '<': # BLTZ
assert op.immediate.kind == INTEGER
let cond = state.stack.pop.i
if cond < 0:
state.branch op.immediate.i
of '>': # BGTZ
assert op.immediate.kind == INTEGER
let cond = state.stack.pop.i
if cond > 0:
state.branch op.immediate.i
of '{': # BLEZ
assert op.immediate.kind == INTEGER
let cond = state.stack.pop.i
if cond <= 0:
state.branch op.immediate.i
of '}': # BGEZ
assert op.immediate.kind == INTEGER
let cond = state.stack.pop.i
if cond >= 0:
state.branch op.immediate.i
of 'g': # FINV
let f = state.stack.pop.f
state.stack.add ArcticType(f: state.pc)
state.pc = f
of '@': # CALL
assert op.immediate.kind == SYMBOL
let f = state.symbols[op.immediate.s]
state.stack.add ArcticType(f: state.pc)
state.pc = f
of ',': # JUMP
assert op.immediate.kind == SYMBOL
let f = state.symbols[op.immediate.s]
state.pc = f
of ';': # RETN
let f = state.stack.pop.f
state.pc = f
# Variable Ops ---------------------------------------------------------
of 'A': # PUTA
state.registers[VARIABLE_A] = state.stack.pop
of 'B': # PUTB
state.registers[VARIABLE_B] = state.stack.pop
of 'C': # PUTC
state.registers[VARIABLE_C] = state.stack.pop
of 'I': # PUTI
state.registers[VARIABLE_I] = state.stack.pop
of 'N': # PUTN
state.registers[VARIABLE_N] = state.stack.pop
of 'X': # PUTX
state.registers[VARIABLE_X] = state.stack.pop
of 'Y': # PUTY
state.registers[VARIABLE_Y] = state.stack.pop
of 'Z': # PUTZ
state.registers[VARIABLE_Z] = state.stack.pop
of 'a': # GETA
state.stack.add state.registers[VARIABLE_A]
of 'b': # GETB
state.stack.add state.registers[VARIABLE_B]
of 'c': # GETC
state.stack.add state.registers[VARIABLE_C]
of 'i': # GETI
state.stack.add state.registers[VARIABLE_I]
of 'n': # GETN
state.stack.add state.registers[VARIABLE_N]
of 'x': # GETX
state.stack.add state.registers[VARIABLE_X]
of 'y': # GETY
state.stack.add state.registers[VARIABLE_Y]
of 'z': # GETZ
state.stack.add state.registers[VARIABLE_Z]
# Miscellaneous Ops ----------------------------------------------------
of '$': # VALU
case op.immediate.kind:
of PLAIN:
state.stack.add ArcticType(i: 0)
of VARIABLE:
state.stack.add state.registers[op.immediate.v]
of INTEGER:
state.stack.add ArcticType(i: op.immediate.i)
of NUMBER:
state.stack.add ArcticType(d: op.immediate.n)
of SYMBOL:
state.stack.add ArcticType(p: state.symbols[op.immediate.s])
of '`': # BIFC
case op.immediate.kind:
of PLAIN:
discard
of VARIABLE:
discard
of INTEGER:
discard # TODO: integer "syscalls" (probably just syscall(n, ...)
of NUMBER:
discard
of SYMBOL:
let callback = builtins[op.immediate.s]
return callback(state)
of ' ': # NOOP
discard
of '\n': # BEAT
discard
else: # Unknown opcode
discard
return CONTINUE

110
src/arctic/types.nim Normal file
View file

@ -0,0 +1,110 @@
import std/[critbits, strformat, tables]
import memory
type
ArcticPointer* = int64
ArcticType* {.union.} = object
b*: int8
s*: int16
w*: int32
i*: int64
u*: uint64
d*: float64
p*: ArcticPointer
f*: ArcticPointer
ArcticVariableIndex* = enum
VARIABLE_A, VARIABLE_B, VARIABLE_C,
VARIABLE_I, VARIABLE_N,
VARIABLE_X, VARIABLE_Y, VARIABLE_Z
ArcticImmediateKind* = enum
PLAIN, VARIABLE, INTEGER, NUMBER, SYMBOL
ArcticImmediate* = object
case kind*: ArcticImmediateKind
of PLAIN: nil
of VARIABLE: v*: ArcticVariableIndex
of INTEGER: i*: int64
of NUMBER: n*: float64
of SYMBOL: s*: string
ArcticOperation* = object
code*: char
immediate*: ArcticImmediate
bigendian*: bool
ArcticStack* = seq[ArcticType]
ArcticState* = object
memory*: Memory[uint8]
code*: Memory[ArcticOperation]
symbols*: CritBitTree[ArcticPointer] # as name or section _name
stack*: ArcticStack
pc*: ArcticPointer # NOT negative!
registers*: array[ArcticVariableIndex, ArcticType]
dynmem*: Table[ArcticPointer, int] # pointer -> length
ArcticStepResult* = enum
CONTINUE, BREAKPOINT, EXIT, ERROR
ArcticBuiltin* = proc (state: var ArcticState): ArcticStepResult {.nimcall.}
proc `$`*(variable: ArcticVariableIndex): string =
case variable:
of VARIABLE_A: return "A"
of VARIABLE_B: return "B"
of VARIABLE_C: return "C"
of VARIABLE_I: return "I"
of VARIABLE_N: return "N"
of VARIABLE_X: return "X"
of VARIABLE_Y: return "Y"
of VARIABLE_Z: return "Z"
proc `$`*(op: ArcticOperation): string =
result &= op.code
case op.immediate.kind:
of PLAIN: discard
of VARIABLE: result &= $op.immediate.v
of INTEGER: result &= $op.immediate.i
of NUMBER: result &= $op.immediate.n
of SYMBOL: result &= $op.immediate.s
proc `$`*(state: ArcticState): string =
result &= "Registers:\n"
for idx in ArcticVariableIndex:
result &= &" {idx} = {state.registers[idx]}\n"
result &= "Stack:\n"
for item in state.stack:
result &= &" {item}\n"
result &= "Sections:\n"
for section in state.code.sections:
result &= "\n section "
result &= section
result &= ":\n"
for (i, m) in state.code.data(section).pairs:
for (label, location) in state.symbols.pairs:
if -location == state.code.address(section, i):
result &= &":{label} "
if state.pc == state.code.address(section, i):
result &= ">"
if m.code != ' ':
result &= $m
for section in state.memory.sections:
result &= "\n section "
result &= section
result &= ":\n"
for (i, m) in state.memory.data(section).pairs:
for (label, location) in state.symbols.pairs:
if location == state.memory.address(section, i):
result &= &":{label} "
result &= &"{m} "