diff --git a/.gitignore b/.gitignore index 33f82a0..308a0dd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ build/ -arctic - +/arctic +htmldocs diff --git a/arctic.nimble b/arctic.nimble index 15424d7..5f8a450 100644 --- a/arctic.nimble +++ b/arctic.nimble @@ -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" diff --git a/src/arctic.nim b/src/arctic.nim index 411c5e6..439548c 100644 --- a/src/arctic.nim +++ b/src/arctic.nim @@ -1,318 +1,10 @@ -import std/[bitops, critbits, macros, strformat, strutils, strmisc, os, tables, math, re] # atomics +import std/[critbits, enumerate, macros, os, tables] + import bio -import itertools -import memory +import arctic/[load, memory, step, types] -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.} - -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', '_', '.', '+', '-', '*', '/', '%', '\\', '|', '&', '^', '!', '?', '(', ')' } - -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} " - -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 - -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 - -var DefaultBuiltins {.compileTime.}: CritBitTree[ArcticBuiltin] +var DefaultBuiltins* {.compileTime.}: CritBitTree[ArcticBuiltin] macro arctic_builtin(builtin: typed): untyped = let nameid = builtin.name @@ -329,740 +21,35 @@ proc putstrln(state: var ArcticState): ArcticStepResult {.arctic_builtin.} = stdout.write('\n') return CONTINUE -proc step*(state: var ArcticState, builtins: CritBitTree[ArcticBuiltin]): ArcticStepResult = - if not (state.pc in state.code): - return EXIT +proc parseCmdLine(): (File, seq[string]) = + let argv = commandLineParams() - let op = state.code[state.pc] - state.pc.inc + if len(argv) > 0: + return (open(argv[0]), argv[1..^0]) + else: + return (stdin, @[]) - 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 when isMainModule: - for fname in DefaultBuiltins.keys: - echo fname - var state = stdin.readAll.load + let (ifile, args) = parseCmdLine() + var state = ifile.readAll.load let builtins = DefaultBuiltins - # TODO: Actually allocate memory and push pointers to cli args - state.stack.add ArcticType(i: 0) - state.stack.add ArcticType(i: 0) - - echo state + var argv: seq[int] = @[] + for arg in args: + let a = state.memory.allocate(arg.len + 1) + for i in 0 .. arg.len: + state.memory[a + i] = arg[i].uint8 + state.memory[a + arg.len] = 0 + argv.add a + let x = state.memory.allocate((args.len + 1) * 8) + for (i, a) in enumerate(argv): + state.memory.write(x + 8 * i, cast[uint64](a.int64).serialize(littleEndian)) + state.memory.write(x + 8 * args.len, 0.uint64.serialize(littleEndian)) + state.stack.add ArcticType(i: 0) # argv + state.stack.add ArcticType(i: args.len) # argc + state.stack.add ArcticType(i: 0) # return address: null while true: case state.step(builtins): @@ -1074,4 +61,3 @@ when isMainModule: break of ERROR: break - diff --git a/src/arctic/constants.nim b/src/arctic/constants.nim new file mode 100644 index 0000000..74aab20 --- /dev/null +++ b/src/arctic/constants.nim @@ -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', '_', '.', '+', '-', '*', '/', '%', '\\', '|', '&', '^', '!', '?', '(', ')' } diff --git a/src/arctic/load.nim b/src/arctic/load.nim new file mode 100644 index 0000000..5f2b3fe --- /dev/null +++ b/src/arctic/load.nim @@ -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 diff --git a/src/memory.nim b/src/arctic/memory.nim similarity index 97% rename from src/memory.nim rename to src/arctic/memory.nim index 7c764a1..37eb185 100644 --- a/src/memory.nim +++ b/src/arctic/memory.nim @@ -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) diff --git a/src/arctic/step.nim b/src/arctic/step.nim new file mode 100644 index 0000000..0c2bcdb --- /dev/null +++ b/src/arctic/step.nim @@ -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 diff --git a/src/arctic/types.nim b/src/arctic/types.nim new file mode 100644 index 0000000..8aede94 --- /dev/null +++ b/src/arctic/types.nim @@ -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} " +