retroforth/vm/nga-python/retro.py

551 lines
11 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
# Nga: a Virtual Machine
# Copyright (c) 2010 - 2019, Charles Childers
# Floating Point I/O by Arland Childers, (c) 2020
# Optimizations and process() rewrite by Greg Copeland
# -----------------------------------------------------
import os, sys, math, time, struct
from struct import pack, unpack
ip = 0
stack = [] * 128
address = []
memory = []
class FloatStack(object):
def __init__(self, *d):
self.data = list(d)
def __getitem__(self, id):
return self.data[id]
def __call__(self):
return self.data
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 ceiling(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()))
floats = FloatStack()
afloats = FloatStack()
def rxDivMod(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 findEntry(named):
header = memory[2]
Done = False
while header != 0 and not Done:
if named == extractString(header + 3):
Done = True
else:
header = memory[header]
return header
def rxGetInput():
return ord(sys.stdin.read(1))
def rxDisplayCharacter():
global stack
if stack[-1] > 0 and stack[-1] < 128:
if stack[-1] == 8:
sys.stdout.write(chr(stack.pop()))
sys.stdout.write(chr(32))
sys.stdout.write(chr(8))
else:
sys.stdout.write(chr(stack.pop()))
else:
sys.stdout.write("\033[2J\033[1;1H")
stack.pop()
sys.stdout.flush()
def i_no():
pass
def i_li():
global ip, memory, stack, address
ip += 1
stack.append(memory[ip])
def i_du():
global ip, memory, stack, address
stack.append(stack[-1])
def i_dr():
global ip, memory, stack, address
stack.pop()
def i_sw():
global ip, memory, stack, address
a = stack[-2]
stack[-2] = stack[-1]
stack[-1] = a
def i_pu():
global ip, memory, stack, address
address.append(stack.pop())
def i_po():
global ip, memory, stack, address
stack.append(address.pop())
def i_ju():
global ip, memory, stack, address
ip = stack.pop() - 1
def i_ca():
global ip, memory, stack, address
address.append(ip)
ip = stack.pop() - 1
def i_cc():
global ip, memory, stack, address
target = stack.pop()
if stack.pop() != 0:
address.append(ip)
ip = target - 1
def i_re():
global ip, memory, stack, address
ip = address.pop()
def i_eq():
global ip, memory, stack, address
a = stack.pop()
b = stack.pop()
if b == a:
stack.append(-1)
else:
stack.append(0)
def i_ne():
global ip, memory, stack, address
a = stack.pop()
b = stack.pop()
if b != a:
stack.append(-1)
else:
stack.append(0)
def i_lt():
global ip, memory, stack, address
a = stack.pop()
b = stack.pop()
if b < a:
stack.append(-1)
else:
stack.append(0)
def i_gt():
global ip, memory, stack, address
a = stack.pop()
b = stack.pop()
if b > a:
stack.append(-1)
else:
stack.append(0)
def i_fe():
global ip, memory, stack, address
if stack[-1] == -1:
stack[-1] = len(stack) - 1
elif stack[-1] == -2:
stack[-1] = len(address)
elif stack[-1] == -3:
stack[-1] = len(memory)
elif stack[-1] == -4:
stack[-1] = -2147483648
elif stack[-1] == -5:
stack[-1] = 2147483647
else:
stack[-1] = memory[stack[-1]]
def i_st():
global ip, memory, stack, address
mi = stack.pop()
memory[mi] = stack.pop()
def i_ad():
global ip, memory, stack, address
t = stack.pop()
stack[-1] += t
stack[-1] = unpack("=l", pack("=L", stack[-1] & 0xFFFFFFFF))[0]
def i_su():
global ip, memory, stack, address
t = stack.pop()
stack[-1] -= t
stack[-1] = unpack("=l", pack("=L", stack[-1] & 0xFFFFFFFF))[0]
def i_mu():
global ip, memory, stack, address
t = stack.pop()
stack[-1] *= t
stack[-1] = unpack("=l", pack("=L", stack[-1] & 0xFFFFFFFF))[0]
def i_di():
global ip, memory, stack, address
a = stack[-1]
b = stack[-2]
stack[-1], stack[-2] = rxDivMod(b, a)
stack[-1] = unpack("=l", pack("=L", stack[-1] & 0xFFFFFFFF))[0]
stack[-2] = unpack("=l", pack("=L", stack[-2] & 0xFFFFFFFF))[0]
def i_an():
global ip, memory, stack, address
t = stack.pop()
stack[-1] &= t
def i_or():
global ip, memory, stack, address
t = stack.pop()
stack[-1] |= t
def i_xo():
global ip, memory, stack, address
t = stack.pop()
stack[-1] ^= t
def i_sh():
global ip, memory, stack, address
t = stack.pop()
if t < 0:
stack[-1] <<= t * -1
else:
stack[-1] >>= t
def i_zr():
global ip, memory, stack, address
if stack[-1] == 0:
stack.pop()
ip = address.pop()
def i_ha():
global ip, memory, stack, address
ip = 9000000
def i_ie():
stack.append(2)
def i_iq():
device = stack.pop()
if device == 0: # generic output
stack.append(0)
stack.append(0)
if device == 1: # floating point
stack.append(1)
stack.append(2)
def i_ii():
device = stack.pop()
if device == 0: # generic output
rxDisplayCharacter()
if device == 1: # floating point
action = stack.pop()
print(floats.data, action)
if action == 0: # number to float
floats.push(float(stack.pop()))
if action == 1: # string to float
floats.push(float(extractString(stack.pop())))
if action == 2: # float to number
stack.append(int(floats.pop()))
if action == 3: # float to string
injectString(str(floats.pop()), stack.pop())
if action == 4: # add
floats.add()
if action == 5: # sub
floats.sub()
if action == 6: # mul
floats.mul()
if action == 7: # div
floats.div()
if action == 8: # floor
floats.floor()
if action == 9: # ceil
floats.ceiling()
if action == 10: # sqrt
floats.sqrt()
if action == 11: # eq
stack.append(floats.eq())
if action == 12: # -eq
stack.append(floats.neq())
if action == 13: # lt
stack.append(floats.lt())
if action == 14: # gt
stack.append(floats.gt())
if action == 15: # depth
stack.append(floats.depth())
if action == 16: # dup
floats.dup()
if action == 17: # drop
floats.drop()
if action == 18: # swap
floats.swap()
if action == 19: # log
floats.log()
if action == 20: # pow
floats.pow()
if action == 21: # sin
floats.sin()
if action == 22: # cos
floats.cos()
if action == 23: # tan
floats.tan()
if action == 24: # asin
floats.asin()
if action == 25: # atan
floats.atan()
if action == 26: # acos
floats.acos()
if action == 27: # to alt.
afloats.push(floats.pop())
if action == 28: # from alt.
floats.push(afloats.pop())
if action == 29: # alt. depth
stack.append(afloats.depth())
instructions = [
i_no,
i_li,
i_du,
i_dr,
i_sw,
i_pu,
i_po,
i_ju,
i_ca,
i_cc,
i_re,
i_eq,
i_ne,
i_lt,
i_gt,
i_fe,
i_st,
i_ad,
i_su,
i_mu,
i_di,
i_an,
i_or,
i_xo,
i_sh,
i_zr,
i_ha,
i_ie,
i_iq,
i_ii,
]
def validateOpcode(opcode):
I0 = opcode & 0xFF
I1 = (opcode >> 8) & 0xFF
I2 = (opcode >> 16) & 0xFF
I3 = (opcode >> 24) & 0xFF
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 extractString(at):
i = at
s = ""
while memory[i] != 0:
s = s + chr(memory[i])
i = i + 1
return s
def injectString(s, to):
global memory
i = to
for c in s:
memory[i] = ord(c)
i = i + 1
memory[i] = 0
def execute(word, output="console"):
global ip, memory, stack, address
ip = word
address.append(0)
notfound = memory[findEntry("err:notfound") + 1]
while ip < 100000 and len(address) > 0:
if ip == notfound:
print("ERROR: word not found!")
opcode = memory[ip]
if validateOpcode(opcode):
I0 = opcode & 0xFF
I1 = (opcode >> 8) & 0xFF
I2 = (opcode >> 16) & 0xFF
I3 = (opcode >> 24) & 0xFF
if I0 != 0:
instructions[I0]()
if I1 != 0:
instructions[I1]()
if I2 != 0:
instructions[I2]()
if I3 != 0:
instructions[I3]()
else:
print("Invalid Bytecode", opcode, ip)
ip = 2000000
ip = ip + 1
return
def load_image():
global memory
cells = int(os.path.getsize("ngaImage") / 4)
f = open("ngaImage", "rb")
memory = list(struct.unpack(cells * "i", f.read()))
f.close()
remaining = 1000000 - cells
memory.extend([0] * remaining)
def run():
Done = False
Interpreter = memory[findEntry("interpret") + 1]
while not Done:
Line = input("\nOk> ")
if Line == "bye":
Done = True
else:
for Token in Line.split(" "):
injectString(Token, 1025)
stack.append(1025)
execute(Interpreter)
if __name__ == "__main__":
load_image()
run()