ce91454df1
FossilOrigin-Name: d6c27c49b054c022fad6d2898d173f0a2705925a97bb139c1c6fbdb48f896da7
823 lines
24 KiB
Python
Executable file
823 lines
24 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
|
|
# Nga: a Virtual Machine
|
|
# Copyright (c) 2010 - 2020, Charles Childers
|
|
# Floating Point I/O by Arland Childers, (c) 2020
|
|
# Optimizations and process() rewrite by Greg Copeland
|
|
# -------------------------------------------------------------
|
|
# This implementation of the VM differs from the reference
|
|
# model in a number of important ways.
|
|
#
|
|
# To aid in performance, it does the following:
|
|
# - caching the Retro dictionary in a Python dict()
|
|
# - replaces some Retro words with implementations in Python
|
|
# - s:eq?
|
|
# - s:length
|
|
# - s:to-number
|
|
# - d:lookup
|
|
#
|
|
# Each major component is managed as a separate class. We have
|
|
# a class for each I/O device, for each stack, and for the
|
|
# memory pool. The main VM is also in a separate class.
|
|
#
|
|
# It's intended that an amalgamation tool will be developed to
|
|
# combine the separate files into a single one for deployment.
|
|
# -------------------------------------------------------------
|
|
|
|
import os, sys, math, time, struct, random, datetime
|
|
|
|
|
|
class Clock:
|
|
def __getitem__(self, id):
|
|
import datetime
|
|
import time
|
|
|
|
now = datetime.datetime.now()
|
|
ids = {
|
|
"time": time.time,
|
|
"year": now.year,
|
|
"month": now.month,
|
|
"day": now.day,
|
|
"hour": now.hour,
|
|
"minute": now.minute,
|
|
"second": now.second,
|
|
# No time_utc?
|
|
"year_utc": now.utcnow().year,
|
|
"month_utc": now.utcnow().month,
|
|
"day_utc": now.utcnow().day,
|
|
"hour_utc": now.utcnow().hour,
|
|
"minute_utc": now.utcnow().minute,
|
|
"second_utc": now.utcnow().second,
|
|
}
|
|
return ids[id]
|
|
|
|
|
|
import random
|
|
|
|
|
|
class RNG:
|
|
def __call__(self):
|
|
return random.randint(-2147483647, 2147483646)
|
|
|
|
|
|
import os
|
|
|
|
|
|
class FileSystem:
|
|
def __init__(self):
|
|
self.files = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
|
|
|
def open(self, params):
|
|
name, mode = params
|
|
slot = 0
|
|
i = 1
|
|
while i < 8:
|
|
if self.files[i] == 0:
|
|
slot = i
|
|
i += 1
|
|
if slot > 0:
|
|
if mode == 0:
|
|
if os.path.exists(name):
|
|
self.files[slot] = open(name, "r")
|
|
else:
|
|
slot = 0
|
|
elif mode == 1:
|
|
self.files[slot] = open(name, "w")
|
|
elif mode == 2:
|
|
self.files[slot] = open(name, "a")
|
|
elif mode == 3:
|
|
if os.path.exists(name):
|
|
self.files[slot] = open(name, "r+")
|
|
else:
|
|
slot = 0
|
|
return slot
|
|
|
|
def read(self, slot):
|
|
return ord(self.files[slot].read(1))
|
|
|
|
def write(self, params):
|
|
slot, char = params
|
|
self.files[slot].write(chr(stack.pop()))
|
|
return 1
|
|
|
|
def close(self, slot):
|
|
self.files[slot].close()
|
|
self.files[slot] = 0
|
|
return 0
|
|
|
|
def pos(self, slot):
|
|
return self.files[slot].tell()
|
|
|
|
def seek(slot, pos):
|
|
return self.files[slot].seek(pos, 0)
|
|
|
|
def size(self, slot):
|
|
at = self.files[slot].tell()
|
|
self.files[slot].seek(0, 2) # SEEK_END
|
|
end = self.files[slot].tell()
|
|
self.files[slot].seek(at, 0) # SEEK_SET
|
|
return end
|
|
|
|
def delete(self, name):
|
|
name = extract_string(stack.pop())
|
|
i = 0
|
|
if os.path.exists(name):
|
|
os.remove(name)
|
|
i = 1
|
|
return i
|
|
|
|
|
|
class FloatStack(object):
|
|
def __init__(self, *d):
|
|
self.data = list(d)
|
|
|
|
def __getitem__(self, id):
|
|
return self.data[id]
|
|
|
|
def add(self):
|
|
self.data.append(self.data.pop() + self.data.pop())
|
|
|
|
def sub(self):
|
|
self.data.append(0 - (self.data.pop() - self.data.pop()))
|
|
|
|
def mul(self):
|
|
self.data.append(self.data.pop() * self.data.pop())
|
|
|
|
def div(self):
|
|
a, b = self.data.pop(), self.data.pop()
|
|
self.data.append(b / a)
|
|
|
|
def ceil(self):
|
|
self.data.append(math.ceil(self.data.pop()))
|
|
|
|
def floor(self):
|
|
self.data.append(math.floor(self.data.pop()))
|
|
|
|
def eq(self):
|
|
return 0 - (self.data.pop() == self.data.pop())
|
|
|
|
def neq(self):
|
|
return 0 - (self.data.pop() != self.data.pop())
|
|
|
|
def gt(self):
|
|
a, b = self.data.pop(), self.data.pop()
|
|
return 0 - (b > a)
|
|
|
|
def lt(self):
|
|
a, b = self.data.pop(), self.data.pop()
|
|
return 0 - (b < a)
|
|
|
|
def depth(self):
|
|
return len(self.data)
|
|
|
|
def drop(self):
|
|
self.data.pop()
|
|
|
|
def pop(self):
|
|
return self.data.pop()
|
|
|
|
def swap(self):
|
|
a, b = self.data.pop(), self.data.pop()
|
|
self.data += [a, b]
|
|
|
|
def push(self, n):
|
|
self.data.append(n)
|
|
|
|
def log(self):
|
|
a, b = self.data.pop(), self.data.pop()
|
|
self.data.append(math.log(b, a))
|
|
|
|
def power(self):
|
|
a, b = self.data.pop(), self.data.pop()
|
|
self.data.append(math.pow(a, b))
|
|
|
|
def sin(self):
|
|
self.data.append(math.sin(self.data.pop()))
|
|
|
|
def cos(self):
|
|
self.data.append(math.cos(self.data.pop()))
|
|
|
|
def tan(self):
|
|
self.data.append(math.tan(self.data.pop()))
|
|
|
|
def asin(self):
|
|
self.data.append(math.asin(self.data.pop()))
|
|
|
|
def acos(self):
|
|
self.data.append(math.acos(self.data.pop()))
|
|
|
|
def atan(self):
|
|
self.data.append(math.atan(self.data.pop()))
|
|
|
|
|
|
class IntegerStack(list):
|
|
def __init__(self):
|
|
stack = [] * 128
|
|
self.extend(stack)
|
|
|
|
def depth(self):
|
|
return len(self)
|
|
|
|
def tos(self):
|
|
return self[-1]
|
|
|
|
def push(self, v):
|
|
self.append(v)
|
|
|
|
def dup(self):
|
|
self.append(self[-1])
|
|
|
|
def drop(self):
|
|
self.pop()
|
|
|
|
def swap(self):
|
|
a = self[-2]
|
|
self[-2] = self[-1]
|
|
self[-1] = a
|
|
|
|
|
|
import os
|
|
import struct
|
|
|
|
|
|
class Memory(list):
|
|
def __init__(self, source, initial, size):
|
|
m = [0] * size
|
|
self.extend(m)
|
|
if len(initial) == 0:
|
|
cells = int(os.path.getsize(source) / 4)
|
|
f = open(source, "rb")
|
|
i = 0
|
|
for cell in list(struct.unpack(cells * "i", f.read())):
|
|
self[i] = cell
|
|
i = i + 1
|
|
f.close()
|
|
else:
|
|
i = 0
|
|
for cell in initial:
|
|
self[i] = cell
|
|
i = i + 1
|
|
|
|
def load_image(self, name):
|
|
cells = int(os.path.getsize(name) / 4)
|
|
f = open(name, "rb")
|
|
i = 0
|
|
for cell in list(struct.unpack(cells * "i", f.read())):
|
|
self[i] = cell
|
|
i = i + 1
|
|
f.close()
|
|
|
|
def size(self):
|
|
return len(self)
|
|
|
|
|
|
InitialImage = []
|
|
|
|
|
|
class Retro:
|
|
def map_in(self, name):
|
|
return self.memory[self.find_entry(name) + 1]
|
|
|
|
def __init__(self):
|
|
self.ip = 0
|
|
self.stack = IntegerStack()
|
|
self.address = IntegerStack()
|
|
self.memory = Memory("ngaImage", InitialImage, 1000000)
|
|
self.clock = Clock()
|
|
self.rng = RNG()
|
|
self.files = FileSystem()
|
|
self.floats = FloatStack()
|
|
self.afloats = FloatStack()
|
|
self.Dictionary = self.populate_dictionary()
|
|
self.Cached = self.cache_words()
|
|
|
|
self.setup_devices()
|
|
self.instructions = [
|
|
self.i_nop,
|
|
self.i_lit,
|
|
self.i_dup,
|
|
self.i_drop,
|
|
self.i_swap,
|
|
self.i_push,
|
|
self.i_pop,
|
|
self.i_jump,
|
|
self.i_call,
|
|
self.i_ccall,
|
|
self.i_return,
|
|
self.i_eq,
|
|
self.i_neq,
|
|
self.i_lt,
|
|
self.i_gt,
|
|
self.i_fetch,
|
|
self.i_store,
|
|
self.i_add,
|
|
self.i_subtract,
|
|
self.i_multiply,
|
|
self.i_divmod,
|
|
self.i_and,
|
|
self.i_or,
|
|
self.i_xor,
|
|
self.i_shift,
|
|
self.i_zreturn,
|
|
self.i_halt,
|
|
self.i_ienumerate,
|
|
self.i_iquery,
|
|
self.i_iinvoke,
|
|
]
|
|
|
|
def div_mod(self, a, b):
|
|
x = abs(a)
|
|
y = abs(b)
|
|
q, r = divmod(x, y)
|
|
if a < 0 and b < 0:
|
|
r *= -1
|
|
elif a > 0 and b < 0:
|
|
q *= -1
|
|
elif a < 0 and b > 0:
|
|
r *= -1
|
|
q *= -1
|
|
return q, r
|
|
|
|
def cache_words(self):
|
|
Cached = dict()
|
|
Cached["interpreter"] = self.map_in("interpret")
|
|
Cached["not_found"] = self.map_in("err:notfound")
|
|
Cached["s:eq?"] = self.map_in("s:eq?")
|
|
Cached["s:to-number"] = self.map_in("s:to-number")
|
|
Cached["s:length"] = self.map_in("s:length")
|
|
Cached["d:lookup"] = self.map_in("d:lookup")
|
|
Cached["d:add-header"] = self.map_in("d:add-header")
|
|
return Cached
|
|
|
|
def populate_dictionary(self):
|
|
Dictionary = dict()
|
|
header = self.memory[2]
|
|
while header != 0:
|
|
named = self.extract_string(header + 4)
|
|
if not named in Dictionary:
|
|
Dictionary[named] = header
|
|
header = self.memory[header]
|
|
return Dictionary
|
|
|
|
def find_entry(self, named):
|
|
if named in self.Dictionary:
|
|
return self.Dictionary[named]
|
|
|
|
header = self.memory[2]
|
|
Done = False
|
|
while header != 0 and not Done:
|
|
if named == self.extract_string(header + 4):
|
|
self.Dictionary[named] = header
|
|
Done = True
|
|
else:
|
|
header = self.memory[header]
|
|
return header
|
|
|
|
def get_input(self):
|
|
return ord(sys.stdin.read(1))
|
|
|
|
def display_character(self):
|
|
if self.stack.tos() > 0 and self.stack.tos() < 128:
|
|
if self.stack.tos() == 8:
|
|
sys.stdout.write(chr(self.stack.pop()))
|
|
sys.stdout.write(chr(32))
|
|
sys.stdout.write(chr(8))
|
|
else:
|
|
sys.stdout.write(chr(self.stack.pop()))
|
|
else:
|
|
sys.stdout.write("\033[2J\033[1;1H")
|
|
self.stack.pop()
|
|
sys.stdout.flush()
|
|
|
|
def i_nop(self):
|
|
pass
|
|
|
|
def i_lit(self):
|
|
self.ip += 1
|
|
self.stack.push(self.memory[self.ip])
|
|
|
|
def i_dup(self):
|
|
self.stack.dup()
|
|
|
|
def i_drop(self):
|
|
self.stack.drop()
|
|
|
|
def i_swap(self):
|
|
self.stack.swap()
|
|
|
|
def i_push(self):
|
|
self.address.push(self.stack.pop())
|
|
|
|
def i_pop(self):
|
|
self.stack.push(self.address.pop())
|
|
|
|
def i_jump(self):
|
|
self.ip = self.stack.pop() - 1
|
|
|
|
def i_call(self):
|
|
self.address.push(self.ip)
|
|
self.ip = self.stack.pop() - 1
|
|
|
|
def i_ccall(self):
|
|
target = self.stack.pop()
|
|
if self.stack.pop() != 0:
|
|
self.address.push(self.ip)
|
|
self.ip = target - 1
|
|
|
|
def i_return(self):
|
|
self.ip = self.address.pop()
|
|
|
|
def i_eq(self):
|
|
a = self.stack.pop()
|
|
b = self.stack.pop()
|
|
if b == a:
|
|
self.stack.push(-1)
|
|
else:
|
|
self.stack.push(0)
|
|
|
|
def i_neq(self):
|
|
a = self.stack.pop()
|
|
b = self.stack.pop()
|
|
if b != a:
|
|
self.stack.push(-1)
|
|
else:
|
|
self.stack.push(0)
|
|
|
|
def i_lt(self):
|
|
a = self.stack.pop()
|
|
b = self.stack.pop()
|
|
if b < a:
|
|
self.stack.push(-1)
|
|
else:
|
|
self.stack.push(0)
|
|
|
|
def i_gt(self):
|
|
a = self.stack.pop()
|
|
b = self.stack.pop()
|
|
if b > a:
|
|
self.stack.push(-1)
|
|
else:
|
|
self.stack.push(0)
|
|
|
|
# The fetch instruction also handles certain
|
|
# introspection queries.
|
|
#
|
|
# Of note is the min and max values for a cell.
|
|
# In most VM implementations, this is limited
|
|
# to 32 bit or 64 bit ranges, but Python allows
|
|
# an unlimited range.
|
|
#
|
|
# I report as if the cells are capped at 128 bits
|
|
# but you can safely ignore this if running on
|
|
# the Python VM. (This does have an impact on
|
|
# floating point values, if using the `e:` words
|
|
# for converting them to/from an encoded format in
|
|
# standard cells, but should not affect anything
|
|
# else in the standard system)
|
|
|
|
def i_fetch_query(self, target):
|
|
if target == -1:
|
|
self.stack.push(self.stack.depth())
|
|
elif target == -2:
|
|
self.stack.push(self.address.depth())
|
|
elif target == -3:
|
|
self.stack.push(self.memory.size())
|
|
elif target == -4:
|
|
self.stack.push(-2147483647)
|
|
elif target == -5:
|
|
self.stack.push(2147483646)
|
|
else:
|
|
raise IndexError
|
|
|
|
def i_fetch(self):
|
|
target = self.stack.pop()
|
|
if target >= 0:
|
|
self.stack.push(self.memory[target])
|
|
else:
|
|
self.i_fetch_query(target)
|
|
|
|
def i_store(self):
|
|
mi = self.stack.pop()
|
|
self.memory[mi] = self.stack.pop()
|
|
|
|
def i_add(self):
|
|
t = self.stack.pop()
|
|
v = self.stack.pop()
|
|
self.stack.push(t + v)
|
|
|
|
def i_subtract(self):
|
|
t = self.stack.pop()
|
|
v = self.stack.pop()
|
|
self.stack.push(v - t)
|
|
|
|
def i_multiply(self):
|
|
t = self.stack.pop()
|
|
v = self.stack.pop()
|
|
self.stack.push(v * t)
|
|
|
|
def i_divmod(self):
|
|
t = self.stack.pop()
|
|
v = self.stack.pop()
|
|
b, a = self.div_mod(v, t)
|
|
self.stack.push(a)
|
|
self.stack.push(b)
|
|
|
|
def i_and(self):
|
|
t = self.stack.pop()
|
|
m = self.stack.pop()
|
|
self.stack.push(m & t)
|
|
|
|
def i_or(self):
|
|
t = self.stack.pop()
|
|
m = self.stack.pop()
|
|
self.stack.push(m | t)
|
|
|
|
def i_xor(self):
|
|
t = self.stack.pop()
|
|
m = self.stack.pop()
|
|
self.stack.push(m ^ t)
|
|
|
|
def i_shift(self):
|
|
t = self.stack.pop()
|
|
v = self.stack.pop()
|
|
|
|
if t < 0:
|
|
v <<= t * -1
|
|
else:
|
|
v >>= t
|
|
|
|
self.stack.push(v)
|
|
|
|
def i_zreturn(self):
|
|
if self.stack.tos() == 0:
|
|
self.stack.pop()
|
|
self.ip = self.address.pop()
|
|
|
|
def i_halt(self):
|
|
self.ip = 9000000
|
|
|
|
def i_ienumerate(self):
|
|
self.stack.push(6)
|
|
|
|
def i_iquery(self):
|
|
device = self.stack.pop()
|
|
if device == 0: # generic output
|
|
self.stack.push(0)
|
|
self.stack.push(0)
|
|
if device == 1: # floating point
|
|
self.stack.push(1)
|
|
self.stack.push(2)
|
|
if device == 2: # files
|
|
self.stack.push(0)
|
|
self.stack.push(4)
|
|
if device == 3: # rng
|
|
self.stack.push(0)
|
|
self.stack.push(10)
|
|
if device == 4: # time
|
|
self.stack.push(0)
|
|
self.stack.push(5)
|
|
if device == 5: # scripting
|
|
self.stack.push(0)
|
|
self.stack.push(9)
|
|
|
|
def file_open_params(self):
|
|
mode = self.stack.pop()
|
|
name = self.extract_string(self.stack.pop())
|
|
return name, mode
|
|
|
|
def file_write_params(self):
|
|
slot = self.stack.pop()
|
|
char = self.stack.pop()
|
|
return slot, char
|
|
|
|
def setup_devices(self):
|
|
self.files_instr = {
|
|
0: lambda: self.stack.push(self.files.open(self.file_open_params())),
|
|
1: lambda: self.files.close(self.stack.pop()),
|
|
2: lambda: self.stack.push(self.files.read(self.stack.pop())),
|
|
3: lambda: self.files.write(self.file_write_params()),
|
|
4: lambda: self.stack.push(self.files.pos(self.stack.pop())),
|
|
5: lambda: self.files.seek(),
|
|
6: lambda: self.stack.push(self.files.size(self.stack.pop())),
|
|
7: lambda: self.files.delete(self.extract_string(self.stack.pop())),
|
|
8: lambda: 1 + 1,
|
|
}
|
|
|
|
self.rng_instr = {0: lambda: self.stack.push(self.rng())}
|
|
|
|
self.clock_instr = {
|
|
0: lambda: self.stack.push(int(time.time())),
|
|
1: lambda: self.stack.push(self.clock["day"]),
|
|
2: lambda: self.stack.push(self.clock["month"]),
|
|
3: lambda: self.stack.push(self.clock["year"]),
|
|
4: lambda: self.stack.push(self.clock["hour"]),
|
|
5: lambda: self.stack.push(self.clock["minute"]),
|
|
6: lambda: self.stack.push(self.clock["second"]),
|
|
7: lambda: self.stack.push(self.clock["day_utc"]),
|
|
8: lambda: self.stack.push(self.clock["month_utc"]),
|
|
9: lambda: self.stack.push(self.clock["year_utc"]),
|
|
10: lambda: self.stack.push(self.clock["hour_utc"]),
|
|
11: lambda: self.stack.push(self.clock["minute_utc"]),
|
|
12: lambda: self.stack.push(self.clock["second_utc"]),
|
|
}
|
|
|
|
self.float_instr = {
|
|
0: lambda: self.floats.push(float(self.stack.pop())),
|
|
1: lambda: self.floats.push(float(self.extract_string(self.stack.pop()))),
|
|
2: lambda: self.stack.push(int(self.floats.pop())),
|
|
3: lambda: self.inject_string(str(self.floats.pop()), self.stack.pop()),
|
|
4: lambda: self.floats.add(),
|
|
5: lambda: self.floats.sub(),
|
|
6: lambda: self.floats.mul(),
|
|
7: lambda: self.floats.div(),
|
|
8: lambda: self.floats.floor(),
|
|
9: lambda: self.floats.ceil(),
|
|
10: lambda: self.floats.sqrt(),
|
|
11: lambda: self.stack.push(self.floats.eq()),
|
|
12: lambda: self.stack.push(self.floats.neq()),
|
|
13: lambda: self.stack.push(self.floats.lt()),
|
|
14: lambda: self.stack.push(self.floats.gt()),
|
|
15: lambda: self.stack.push(self.floats.depth()),
|
|
16: lambda: self.floats.dup(),
|
|
17: lambda: self.floats.drop(),
|
|
18: lambda: self.floats.swap(),
|
|
19: lambda: self.floats.log(),
|
|
20: lambda: self.floats.pow(),
|
|
21: lambda: self.floats.sin(),
|
|
22: lambda: self.floats.cos(),
|
|
23: lambda: self.floats.tan(),
|
|
24: lambda: self.floats.asin(),
|
|
25: lambda: self.floats.atan(),
|
|
26: lambda: self.floats.acos(),
|
|
27: lambda: self.afloats.push(self.floats.pop()),
|
|
28: lambda: self.floats.push(self.afloats.pop()),
|
|
29: lambda: self.stack.push(self.afloats.depth()),
|
|
}
|
|
|
|
def i_iinvoke(self):
|
|
device = self.stack.pop()
|
|
# print('dev:', device)
|
|
if device == 0:
|
|
self.display_character()
|
|
if device == 1:
|
|
action = self.stack.pop()
|
|
self.float_instr[int(action)]()
|
|
if device == 2:
|
|
action = self.stack.pop()
|
|
self.files_instr[int(action)]()
|
|
if device == 3:
|
|
self.rng_instr[0]()
|
|
if device == 4:
|
|
action = self.stack.pop()
|
|
self.clock_instr[int(action)]()
|
|
if device == 5:
|
|
action = self.stack.pop()
|
|
if action == 0:
|
|
self.stack.push(len(sys.argv) - 2)
|
|
if action == 1:
|
|
a = self.stack.pop()
|
|
b = self.stack.pop()
|
|
self.stack.push(self.inject_string(sys.argv[a + 2], b))
|
|
if action == 2:
|
|
self.run_file(self.extract_string(self.stack.pop()))
|
|
if action == 3:
|
|
b = self.stack.pop()
|
|
self.stack.push(self.inject_string(sys.argv[0], b))
|
|
|
|
def validate_opcode(self, I0, I1, I2, I3):
|
|
if (
|
|
(I0 >= 0 and I0 <= 29)
|
|
and (I1 >= 0 and I1 <= 29)
|
|
and (I2 >= 0 and I2 <= 29)
|
|
and (I3 >= 0 and I3 <= 29)
|
|
):
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
def extract_string(self, at):
|
|
s = ""
|
|
while self.memory[at] != 0:
|
|
s = s + chr(self.memory[at])
|
|
at = at + 1
|
|
return s
|
|
|
|
def inject_string(self, s, to):
|
|
for c in s:
|
|
self.memory[to] = ord(c)
|
|
to = to + 1
|
|
self.memory[to] = 0
|
|
|
|
def execute(self, word, notfound):
|
|
self.ip = word
|
|
if self.address.depth() == 0:
|
|
self.address.push(0)
|
|
while self.ip < 1000000:
|
|
if self.ip == self.Cached["s:eq?"]:
|
|
a = self.extract_string(self.stack.pop())
|
|
b = self.extract_string(self.stack.pop())
|
|
if a == b:
|
|
self.stack.push(-1)
|
|
else:
|
|
self.stack.push(0)
|
|
self.ip = self.address.pop()
|
|
elif self.ip == self.Cached["d:lookup"]:
|
|
name = self.extract_string(self.stack.pop())
|
|
header = self.find_entry(name)
|
|
self.stack.push(header)
|
|
self.memory[self.Cached["d:lookup"] - 20] = header # "which"
|
|
self.ip = self.address.pop()
|
|
elif self.ip == self.Cached["s:to-number"]:
|
|
n = self.extract_string(self.stack.pop())
|
|
self.stack.push(int(n))
|
|
self.ip = self.address.pop()
|
|
elif self.ip == self.Cached["s:length"]:
|
|
n = len(self.extract_string(self.stack.pop()))
|
|
self.stack.push(n)
|
|
self.ip = self.address.pop()
|
|
else:
|
|
if self.ip == notfound:
|
|
print("ERROR: word not found!")
|
|
if self.ip == self.Cached["d:add-header"]:
|
|
self.Dictionary[self.extract_string(self.stack[-3])] = self.memory[
|
|
3
|
|
]
|
|
opcode = self.memory[self.ip]
|
|
I0 = opcode & 0xFF
|
|
I1 = (opcode >> 8) & 0xFF
|
|
I2 = (opcode >> 16) & 0xFF
|
|
I3 = (opcode >> 24) & 0xFF
|
|
if self.validate_opcode(I0, I1, I2, I3):
|
|
# print("Bytecode: ", I0, I1, I2, I3, "at", self.ip)
|
|
if I0 != 0:
|
|
self.instructions[I0]()
|
|
if I1 != 0:
|
|
self.instructions[I1]()
|
|
if I2 != 0:
|
|
self.instructions[I2]()
|
|
if I3 != 0:
|
|
self.instructions[I3]()
|
|
else:
|
|
print("Invalid Bytecode: ", opcode, "at", self.ip)
|
|
self.ip = 2000000
|
|
if self.address.depth() == 0:
|
|
self.ip = 2000000
|
|
self.ip = self.ip + 1
|
|
return
|
|
|
|
def run(self):
|
|
done = False
|
|
while not done:
|
|
line = input("\nOk> ")
|
|
if line == "bye":
|
|
done = True
|
|
else:
|
|
for token in line.split():
|
|
self.inject_string(token, self.memory[7])
|
|
self.stack.push(self.memory[7])
|
|
self.execute(self.Cached["interpreter"], self.Cached["not_found"])
|
|
|
|
def run_file(self, file):
|
|
if not os.path.exists(file):
|
|
print("File '{0}' not found".format(file))
|
|
return
|
|
|
|
in_block = False
|
|
with open(file, "r") as source:
|
|
for line in source.readlines():
|
|
if line.rstrip() == "~~~":
|
|
in_block = not in_block
|
|
elif in_block:
|
|
for token in line.strip().split():
|
|
self.inject_string(token, self.memory[7])
|
|
self.stack.push(self.memory[7])
|
|
self.execute(
|
|
self.Cached["interpreter"], self.Cached["not_found"]
|
|
)
|
|
|
|
def update_image(self):
|
|
import requests
|
|
import shutil
|
|
|
|
data = requests.get("https://forthworks.com/retro/ngaImage", stream=True)
|
|
with open("ngaImage", "wb") as f:
|
|
data.raw.decode_content = True
|
|
shutil.copyfileobj(data.raw, f)
|
|
|
|
def save_image(self):
|
|
print("Writing {0} cells to {1}".format(self.memory[3], sys.argv[1]))
|
|
with open(sys.argv[1], "wb") as file:
|
|
j = 0
|
|
while j <= self.memory[3]:
|
|
cell = struct.unpack(
|
|
"=l", struct.pack("=L", self.memory[j] & 0xFFFFFFFF)
|
|
)[0]
|
|
file.write(struct.pack("i", cell))
|
|
j = j + 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
retro = Retro()
|
|
for f in sys.argv[2:]:
|
|
retro.run_file(f)
|
|
retro.save_image()
|