diff --git a/doc/documentation.md b/doc/documentation.md index 62303fb..13c0873 100644 --- a/doc/documentation.md +++ b/doc/documentation.md @@ -465,7 +465,7 @@ These can be combined to store the entire stack in a new array via: `(w$0 Rt)` { |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 +@ |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 diff --git a/samples/hello.ctc b/samples/hello.ctc index f1ed7cd..cb63bb0 100644 --- a/samples/hello.ctc +++ b/samples/hello.ctc @@ -2,12 +2,9 @@ @main -"with stdio" - "data" -:message 'Hello, World!' +:message '"Hello, World!\x00"' "code" :main T3 kk # main is called as: &argv[0] argc retaddr -$message @putstrln $0; - +$message `putstrln $0 ; diff --git a/src/arctic.nim b/src/arctic.nim index 22fe8ec..411c5e6 100644 --- a/src/arctic.nim +++ b/src/arctic.nim @@ -1,4 +1,4 @@ -import std/[atomics, bitops, critbits, strformat, strutils, strmisc, tables, math, re] +import std/[bitops, critbits, macros, strformat, strutils, strmisc, os, tables, math, re] # atomics import bio import itertools @@ -14,9 +14,8 @@ type i: int64 u: uint64 d: float64 - p: ArcticPointer # always positive (null is 0) - f: ArcticPointer # always negative (null is 0) - # TODO: check that appropriate p/f is used (there are a lot of legacy .f's hanging around) + p: ArcticPointer + f: ArcticPointer ArcticVariableIndex = enum VARIABLE_A, VARIABLE_B, VARIABLE_C, @@ -51,13 +50,13 @@ type dynmem: Table[ArcticPointer, int] # pointer -> length ArcticStepResult* = enum - CONTINUE, BREAKPOINT, ERROR + CONTINUE, BREAKPOINT, EXIT, ERROR - ArcticBuiltin* = proc (state: var ArcticState): ArcticStepResult + 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', '_', '.', '+', '-', '*', '/', '%', '\\', '|', '&', '^', '!', '?', '(', ')', '`' } + 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: @@ -78,9 +77,7 @@ proc `$`*(op: ArcticOperation): string = of VARIABLE: result &= $op.immediate.v of INTEGER: result &= $op.immediate.i of NUMBER: result &= $op.immediate.n - of SYMBOL: result &= "$" & $op.immediate.s - - result &= " " + of SYMBOL: result &= $op.immediate.s proc `$`*(state: ArcticState): string = result &= "Registers:\n" @@ -92,27 +89,31 @@ proc `$`*(state: ArcticState): string = for item in state.stack: result &= &" {item}\n" + result &= "Sections:\n" + for section in state.code.sections: - result &= "\n" + 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" + 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 op(code: char, immediate: ArcticImmediate = ArcticImmediate(kind: PLAIN)): ArcticOperation = - return ArcticOperation(code: code, immediate: immediate) - proc tovar(code: char): ArcticVariableIndex = case code.toLowerAscii: of 'a': VARIABLE_A @@ -130,13 +131,13 @@ proc parse_immediate(imm: string, section: string): ArcticImmediate = return ArcticImmediate(kind: INTEGER, i: imm.parseInt) elif imm.match(re"^[AaBbCcIiNnXxYyZz]$"): return ArcticImmediate(kind: VARIABLE, v: imm[0].tovar) - elif imm[0] == '$': - if imm.len == 1 or imm[1] == '_': + 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) - else: - return ArcticImmediate(kind: NUMBER, n: imm.parseFloat) func grabnum(data: string): (string, string) = ## Returns the numeric literal at the start of data, then the rest of data @@ -192,7 +193,7 @@ proc parse_data(data: string): seq[uint8] = result.add parse_data(rest) of 'x': # hexadecimal byte constant - let (hex, x, rest) = data[1..^1].partition("x") + 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) @@ -200,7 +201,7 @@ proc parse_data(data: string): seq[uint8] = of '"': # utf-8 string constant with escapes let strlen = data.matchLen(re"^""([^""]|\\"")*""") assert strlen >= 0 - for c in unescape(data[1..strlen]): + for c in unescape(data[1..strlen - 2], prefix="", suffix=""): result.add c.uint8 result.add parse_data(data[strlen..^1]) @@ -219,7 +220,7 @@ func isbigendian(secname: string): bool = func raw_op(code: char): ArcticOperation = result.code = code - result.immediate.kind = PLAIN + result.immediate = ArcticImmediate(kind: PLAIN) func load*(code: string): ArcticState = var @@ -229,7 +230,6 @@ func load*(code: string): ArcticState = result.pc = 1 for next in code: - debug_echo "token: ", token, ", next: ", next if token.len == 0: # initial state case next: of ImmediateOps, '#', ':', '\"', '\'': @@ -256,7 +256,7 @@ func load*(code: string): ArcticState = 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) + result.symbols[idx] = result.code.current(section) else: result.symbols[idx] = result.code.current(section) token = "" @@ -287,7 +287,7 @@ func load*(code: string): ArcticState = token.add next of ImmediateOps: - if next == ' ': + if next in " \n": result.code.add(section, ArcticOperation( code: token[0], immediate: parse_immediate(token[1..^1], section), @@ -299,7 +299,40 @@ func load*(code: string): ArcticState = 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] + +macro arctic_builtin(builtin: typed): untyped = + let nameid = builtin.name + let namestr = newLit(nameid.strVal) + + result = quote do: + `builtin` + DefaultBuiltins[`namestr`] = `nameid` + +proc putstrln(state: var ArcticState): ArcticStepResult {.arctic_builtin.} = + var p = state.stack.pop.p + while state.memory[p] != 0: + stdout.write(state.memory[p]) + stdout.write('\n') + return CONTINUE + 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 @@ -311,7 +344,7 @@ proc step*(state: var ArcticState, builtins: CritBitTree[ArcticBuiltin]): Arctic of '2': # LDAS let - p = state.stack.pop.f + p = state.stack.pop.p a = state.memory[p].int64 b = state.memory[p+1].int64 if op.bigendian: @@ -321,7 +354,7 @@ proc step*(state: var ArcticState, builtins: CritBitTree[ArcticBuiltin]): Arctic of '3': # LDAI let - p = state.stack.pop.f + p = state.stack.pop.p a = state.memory[p].int64 b = state.memory[p+1].int64 c = state.memory[p+2].int64 @@ -333,7 +366,7 @@ proc step*(state: var ArcticState, builtins: CritBitTree[ArcticBuiltin]): Arctic of '4': # LDAW let - p = state.stack.pop.f + p = state.stack.pop.p a = state.memory[p].int64 b = state.memory[p+1].int64 c = state.memory[p+2].int64 @@ -349,13 +382,13 @@ proc step*(state: var ArcticState, builtins: CritBitTree[ArcticBuiltin]): Arctic of 'o': # LDOB assert op.immediate.kind == INTEGER - let p = state.stack.pop.f + 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.f + op.immediate.i + p = state.stack.pop.p + op.immediate.i a = state.memory[p].int64 b = state.memory[p+1].int64 if op.bigendian: @@ -365,7 +398,7 @@ proc step*(state: var ArcticState, builtins: CritBitTree[ArcticBuiltin]): Arctic of 'G': # LDOI let - p = state.stack.pop.f + op.immediate.i + 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 @@ -377,7 +410,7 @@ proc step*(state: var ArcticState, builtins: CritBitTree[ArcticBuiltin]): Arctic of 'Q': # LDOW let - p = state.stack.pop.f + p = state.stack.pop.p a = state.memory[p].int64 b = state.memory[p+1].int64 c = state.memory[p+2].int64 @@ -394,13 +427,13 @@ proc step*(state: var ArcticState, builtins: CritBitTree[ArcticBuiltin]): Arctic of '5': # STAB let x = cast[uint8](state.stack.pop.b) - p = state.stack.pop.f + p = state.stack.pop.p state.memory[p] = x of '6': # STAS let x = cast[uint16](state.stack.pop.s) - p = state.stack.pop.f + p = state.stack.pop.p if op.bigendian: state.memory[p] = uint8(x and 0xFF) state.memory[p+1] = uint8(x shr 8) @@ -411,7 +444,7 @@ proc step*(state: var ArcticState, builtins: CritBitTree[ArcticBuiltin]): Arctic of '7': # STAI let x = cast[uint32](state.stack.pop.i) - p = state.stack.pop.f + 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) @@ -426,7 +459,7 @@ proc step*(state: var ArcticState, builtins: CritBitTree[ArcticBuiltin]): Arctic of '8': # STAW let x = cast[uint64](state.stack.pop.i) - p = state.stack.pop.f + 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) @@ -449,7 +482,7 @@ proc step*(state: var ArcticState, builtins: CritBitTree[ArcticBuiltin]): Arctic of 'D': # MCLR let x = state.stack.pop.i - p = state.stack.pop.f + p = state.stack.pop.p for i in 0 .. x: state.memory[p+i] = 0 @@ -457,7 +490,7 @@ proc step*(state: var ArcticState, builtins: CritBitTree[ArcticBuiltin]): Arctic assert op.immediate.kind == INTEGER let x = state.stack.pop.i - p = state.stack.pop.f + p = state.stack.pop.p b = op.immediate.i.uint8 for i in 0 .. x: state.memory[p+i] = b @@ -465,8 +498,8 @@ proc step*(state: var ArcticState, builtins: CritBitTree[ArcticBuiltin]): Arctic of 'K': # MCPY let x = state.stack.pop.i - q = state.stack.pop.f - p = state.stack.pop.f + q = state.stack.pop.p + p = state.stack.pop.p for i in 0 .. x: state.memory[q+i] = state.memory[p+i] @@ -518,7 +551,7 @@ proc step*(state: var ArcticState, builtins: CritBitTree[ArcticBuiltin]): Arctic let y = state.stack.pop.i x = state.stack.pop.i - p = state.stack.pop.f + p = state.stack.pop.p a = state.memory[p].int64 b = state.memory[p+1].int64 c = state.memory[p+2].int64 @@ -706,6 +739,7 @@ proc step*(state: var ArcticState, builtins: CritBitTree[ArcticBuiltin]): Arctic let v = op.immediate.v i = state.registers[v].i else: + echo "Unexpected immediate: ", op assert false if i > 0: @@ -859,18 +893,185 @@ proc step*(state: var ArcticState, builtins: CritBitTree[ArcticBuiltin]): Arctic state.stack.add ArcticType(i: state.stack.len) of ')': # PACK - let - x = state.stack.pop.i - 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+... + 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 ---------------------------------------------------------- - # TODO + of '=': # BEQZ + assert op.immediate.kind == INTEGER + let cond = state.stack.pop.i - # TODO + 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: - let state = stdin.readAll.load + for fname in DefaultBuiltins.keys: + echo fname + var state = stdin.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 + + while true: + case state.step(builtins): + of CONTINUE: + continue + of BREAKPOINT: + echo state + of EXIT: + break + of ERROR: + break + diff --git a/src/memory.nim b/src/memory.nim new file mode 100644 index 0000000..7c764a1 --- /dev/null +++ b/src/memory.nim @@ -0,0 +1,303 @@ +import std/[critbits, math, tables] + +type + Memory*[T] = object + ## Memory holds a set of sections and maps addresses in those sections + ## to the range (0, int.high) via a low discrepancy sequence (in this + ## case, the van der corput sequence) + chunks: seq[seq[T]] + sects: CritBitTree[int] # which chunk is the section + # TODO: keep track of lengths and "skip" vdc indices that would overlap + # (only an issue where e.g. section 0 is huge and there are lots + # of sections) + smlmem: Table[int, seq[T]] # index -> buffer + midmem: Table[int, seq[T]] # index -> buffer + bigmem: Table[int, seq[T]] # index -> buffer + hugmem: Table[int, seq[T]] # index -> buffer + # dynamic addresses are: + # (1 shl 18)-element blocks x (1 shl 42) starting from (int.high shr 1) + # (1 shl 26)-element blocks x (1 shl 34) starting from (int.high shr 1) + (1 shl 60) + # (1 shl 34)-element blocks x (1 shl 26) starting from (int.high shr 1) + 2 * (1 shl 60) + # (1 shl 42)-element blocks x (1 shl 18) starting from (int.high shr 1) + 3 * (1 shl 60) + +const + HUG_BLOCK_SIZE = 1 shl 42 + BIG_BLOCK_SIZE = 1 shl 34 + MID_BLOCK_SIZE = 1 shl 26 + SML_BLOCK_SIZE = 1 shl 18 + + HUG_BLOCK_COUNT = 1 shl 18 + BIG_BLOCK_COUNT = 1 shl 26 + MID_BLOCK_COUNT = 1 shl 34 + SML_BLOCK_COUNT = 1 shl 42 + + SML_BLOCK_START = int.high shr 1 + MID_BLOCK_START = SML_BLOCK_START + (1 shl 60) + BIG_BLOCK_START = MID_BLOCK_START + (1 shl 60) + HUG_BLOCK_START = BIG_BLOCK_START + (1 shl 60) + +proc van_der_corput(n: int): int = + var + q = 1 # 0 is reserved for null + i = n + b = int.high shr 2 # int.high/2 .. int.high is reserved for dynmem + + while i > 0: + q += (i and 1) * b + i = i shr 1 + b = b shr 1 + + return q + +proc add*[T](memory: var Memory[T], section: string, value: T) = + ## Adds value to the end of memory in section. + if not (section in memory.sects): + memory.sects[section] = memory.chunks.len + memory.chunks.add @[] + + memory.chunks[memory.sects[section]].add value + +proc add*[T](memory: var Memory[T], section: string, values: openarray[T]) = + ## Adds value to the end of memory in section. + if not (section in memory.sects): + memory.sects[section] = memory.chunks.len + memory.chunks.add @[] + + memory.chunks[memory.sects[section]].add values + +proc address*[T](memory: Memory[T], section: string, offset: int): int = + ## Computes the "real" address of a given section and offset + return van_der_corput(memory.sects[section]) + offset + +proc current*[T](memory: Memory[T], section: string): int = + ## Computes the "real" address of the "next" element in the section + let i = memory.sects[section] + return van_der_corput(i) + len(memory.chunks[i]) + +proc addrinfo[T](memory: Memory[T], address: int): (int, int) = + ## Calculates which section index an address points to and the offset in it + var + index = 0 + start = van_der_corput(index) + + for i in 1 .. memory.chunks.len: + let corput = van_der_corput(i) + if (corput < address) and (corput > start): + index = i + start = corput + + return (index, address - start) + +proc contains*[T](memory: Memory[T], address: int): bool = + ## Checks if the given address is a valid allocated address + if address < SML_BLOCK_START: + let (idx, off) = memory.addrinfo(address) + return memory.chunks[idx].len >= off + elif address < MID_BLOCK_START: + let (blkid, offset) = divmod(address - SML_BLOCK_START, SML_BLOCK_SIZE) + return memory.smlmem[blkid].len >= offset + elif address < BIG_BLOCK_START: + let (blkid, offset) = divmod(address - MID_BLOCK_START, MID_BLOCK_SIZE) + return memory.midmem[blkid].len >= offset + elif address < HUG_BLOCK_START: + let (blkid, offset) = divmod(address - BIG_BLOCK_START, BIG_BLOCK_SIZE) + return memory.bigmem[blkid].len >= offset + else: + let (blkid, offset) = divmod(address - HUG_BLOCK_START, HUG_BLOCK_SIZE) + return memory.hugmem[blkid].len >= offset + +proc `[]`*[T](memory: Memory[T], address: int): T = + ## Accesses the underlying memory item for the given address, failing if it + ## does not exist + if address < SML_BLOCK_START: + let (idx, off) = memory.addrinfo(address) + return memory.chunks[idx][off] + elif address < MID_BLOCK_START: + let (blkid, offset) = divmod(address - SML_BLOCK_START, SML_BLOCK_SIZE) + return memory.smlmem[blkid][offset] + elif address < BIG_BLOCK_START: + let (blkid, offset) = divmod(address - MID_BLOCK_START, MID_BLOCK_SIZE) + return memory.midmem[blkid][offset] + elif address < HUG_BLOCK_START: + let (blkid, offset) = divmod(address - BIG_BLOCK_START, BIG_BLOCK_SIZE) + return memory.bigmem[blkid][offset] + else: + let (blkid, offset) = divmod(address - HUG_BLOCK_START, HUG_BLOCK_SIZE) + return memory.hugmem[blkid][offset] + +proc `[]`*[T](memory: var Memory[T], address: int): var T = + ## Accesses the underlying memory item for the given address, failing if it + ## does not exist + if address < SML_BLOCK_START: + let (idx, off) = memory.addrinfo(address) + return memory.chunks[idx][off] + elif address < MID_BLOCK_START: + let (blkid, offset) = divmod(address - SML_BLOCK_START, SML_BLOCK_SIZE) + return memory.smlmem[blkid][offset] + elif address < BIG_BLOCK_START: + let (blkid, offset) = divmod(address - MID_BLOCK_START, MID_BLOCK_SIZE) + return memory.midmem[blkid][offset] + elif address < HUG_BLOCK_START: + let (blkid, offset) = divmod(address - BIG_BLOCK_START, BIG_BLOCK_SIZE) + return memory.bigmem[blkid][offset] + else: + let (blkid, offset) = divmod(address - HUG_BLOCK_START, HUG_BLOCK_SIZE) + return memory.hugmem[blkid][offset] + +proc `[]=`*[T](memory: var Memory[T], address: int, value: T) = + ## Writes to the underlying memory item for a given address, failing if it + ## does not exist + if address < SML_BLOCK_START: + let (idx, off) = memory.addrinfo(address) + memory.chunks[idx][off] = value + elif address < MID_BLOCK_START: + let (blkid, offset) = divmod(address - SML_BLOCK_START, SML_BLOCK_SIZE) + memory.smlmem[blkid][offset] = value + elif address < BIG_BLOCK_START: + let (blkid, offset) = divmod(address - MID_BLOCK_START, MID_BLOCK_SIZE) + memory.midmem[blkid][offset] = value + elif address < HUG_BLOCK_START: + let (blkid, offset) = divmod(address - BIG_BLOCK_START, BIG_BLOCK_SIZE) + memory.bigmem[blkid][offset] = value + else: + let (blkid, offset) = divmod(address - HUG_BLOCK_START, HUG_BLOCK_SIZE) + memory.hugmem[blkid][offset] = value + +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) + + for (section, index) in memory.sects.pairs: + if index == idx: + return section + + return "" + +iterator sections*[T](memory: Memory[T]): string = + for section in memory.sects.keys: + yield section + +proc data*[T](memory: Memory[T], section: string): seq[T] = + return memory.chunks[memory.sects[section]] + +proc allocate*[T](memory: var Memory[T], count: int): int = + ## Allocates a block of count values that can be addressed starting from the + ## returned value + if count < SML_BLOCK_SIZE: + var i = 0 + while i in memory.smlmem: + inc i + if i > SML_BLOCK_COUNT: + raise new ResourceExhaustedError + memory.smlmem[i] = newSeq[T](count) + return SML_BLOCK_START + SML_BLOCK_SIZE * i + elif count < MID_BLOCK_SIZE: + var i = 0 + while i in memory.smlmem: + inc i + if i > MID_BLOCK_COUNT: + raise new ResourceExhaustedError + memory.smlmem[i] = newSeq[T](count) + return MID_BLOCK_START + MID_BLOCK_SIZE * i + elif count < BIG_BLOCK_SIZE: + var i: int = 0 + while i in memory.smlmem: + inc i + if i > BIG_BLOCK_COUNT: + raise new ResourceExhaustedError + memory.smlmem[i] = newSeq[T](count) + return BIG_BLOCK_START + BIG_BLOCK_SIZE * i + else: + var i = 0 + while i in memory.smlmem: + inc i + if i > HUG_BLOCK_COUNT: + raise new ResourceExhaustedError + memory.smlmem[i] = newSeq[T](count) + return HUG_BLOCK_START + HUG_BLOCK_SIZE * i + +proc deallocate*[T](memory: var Memory[T], address: int) = + ## Deallocates the block containing address + if address < SML_BLOCK_START: + discard # Can't deallocate static memory + elif address < MID_BLOCK_START: + let blkid = (address - SML_BLOCK_START) div SML_BLOCK_SIZE + memory.smlmem[blkid].setLen(0) + memory.smlmem.del(blkid) + elif address < BIG_BLOCK_START: + let blkid = (address - MID_BLOCK_START) div MID_BLOCK_SIZE + memory.midmem[blkid].setLen(0) + memory.midmem.del(blkid) + elif address < HUG_BLOCK_START: + let blkid = (address - BIG_BLOCK_START) div BIG_BLOCK_SIZE + memory.bigmem[blkid].setLen(0) + memory.bigmem.del(blkid) + else: + let blkid = (address - HUG_BLOCK_START) div HUG_BLOCK_SIZE + memory.hugmem[blkid].setLen(0) + memory.hugmem.del(blkid) + +proc reallocate*[T](memory: var Memory[T], address: int, count: int): int = + ## Reallocates the block containing address to store up to count items + var theseq: seq[T] + if address < SML_BLOCK_START: + return address # Can't reallocate static memory + elif address < MID_BLOCK_START: + let blkid = (address - SML_BLOCK_START) div SML_BLOCK_SIZE + if count <= SML_BLOCK_SIZE: + memory.smlmem[blkid].setLen(count) + return address + assert memory.smlmem.pop(blkid, theseq) + elif address < BIG_BLOCK_START: + let blkid = (address - MID_BLOCK_START) div MID_BLOCK_SIZE + if count <= MID_BLOCK_SIZE: + memory.midmem[blkid].setLen(count) + return address + assert memory.midmem.pop(blkid, theseq) + elif address < HUG_BLOCK_START: + let blkid = (address - BIG_BLOCK_START) div BIG_BLOCK_SIZE + if count <= BIG_BLOCK_SIZE: + memory.bigmem[blkid].setLen(count) + return address + assert memory.bigmem.pop(blkid, theseq) + else: + let blkid = (address - HUG_BLOCK_START) div HUG_BLOCK_SIZE + if count <= HUG_BLOCK_SIZE: + memory.hugmem[blkid].setLen(count) + return address + assert memory.hugmem.pop(blkid, theseq) + + theseq.setLen(count) + + if count < SML_BLOCK_SIZE: + var i = 0 + while i in memory.smlmem: + inc i + if i > SML_BLOCK_COUNT: + raise new ResourceExhaustedError + memory.smlmem[i] = theseq + return SML_BLOCK_START + SML_BLOCK_SIZE * i + elif count < MID_BLOCK_SIZE: + var i = 0 + while i in memory.smlmem: + inc i + if i > MID_BLOCK_COUNT: + raise new ResourceExhaustedError + memory.smlmem[i] = theseq + return MID_BLOCK_START + MID_BLOCK_SIZE * i + elif count < BIG_BLOCK_SIZE: + var i = 0 + while i in memory.smlmem: + inc i + if i > BIG_BLOCK_COUNT: + raise new ResourceExhaustedError + memory.smlmem[i] = theseq + return BIG_BLOCK_START + BIG_BLOCK_SIZE * i + else: + var i = 0 + while i in memory.smlmem: + inc i + if i > HUG_BLOCK_COUNT: + raise new ResourceExhaustedError + memory.smlmem[i] = theseq + return HUG_BLOCK_START + HUG_BLOCK_SIZE * i