Files
n808/n808.py
T

133 lines
5.6 KiB
Python
Raw Normal View History

2025-05-04 11:46:54 +03:00
#!/usr/bin/env python3
# n808 VM reference implementation in Python 3/MicroPython
# Supports the entire n808 spec
# See README.md for all documentation
# Created by Luxferre in 2025, released into public domain
import sys, math, random, re
advterm = False
try:
import tty, termios # for the character input port
advterm = True
except:
pass
MEMLIMIT:int = 128
PMEM = [0 for i in range(0, MEMLIMIT)] # program memory
DMEM = [0.0 for i in range(0, MEMLIMIT)] # data memory
# port I/O function
def portio(port:int, data:float):
if port == 0: print(data) # standard numeric output
elif port == 1: # standard numeric input
try: data = float(input())
except ValueError: data = 0
elif port == 2: # character output
sys.stdout.write(chr(int(data)&255))
sys.stdout.flush()
elif port == 3: # character input
ch = '\0'
if advterm: # normal OS with termios
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
try:
tty.setraw(fd)
ch = sys.stdin.read(1)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
else: # crippled OS without termios
ch = sys.stdin.read(1)
data = float(ord(ch))
return data
# main instruction execution logic
def iexec(lno:int):
global PMEM, DMEM
data_override = 0
while True: # decode and execute the current instruction
cmd = (PMEM[lno] >> 21) & 7 # command opcode
p1 = (int(v1) if data_override else (PMEM[lno] >> 14)) & 127 # parameter 1
p2 = (int(v2) if data_override else (PMEM[lno] >> 7)) & 127 # parameter 2
p3 = (int(v3) if data_override else PMEM[lno]) & 127 # parameter 3
DMEM[0] = data_override = 0 # enforce the 0 at the location 0
DMEM[127] = 1 # enforce the 1 at the location 127
DMEM[126] = -1 # enforce the -1 at the location 126
v1 = DMEM[p1]; v2 = DMEM[p2]; v3 = DMEM[p3] # prefetch the values
if cmd == 1: # JMP
if p1 == 14: DMEM[125] = lno + 1
if (v2 == 0 and p1 == 0) or (v2 > 0 and p1 == 1) or (v2 < 0 and p1 == 2) \
or (v2 >= 0 and p1 == 3) or (v2 <= 0 and p1 == 4) or (v2 != 0 and p1 == 5) \
or p1 == 6 or p1 == 14: lno = p3 - 1
elif (v2 == 0 and p1 == 7) or (v2 > 0 and p1 == 8) or (v2 < 0 and p1 == 9) \
or (v2 >= 0 and p1 == 10) or (v2 <= 0 and p1 == 11) or (v2 != 0 and p1 == 12) \
or p1 == 13: lno = int(v3) - 1
elif cmd == 2: data_override = 1 # IAT
elif cmd == 3: # INO
for i in range(p2,p3+1): DMEM[i] = portio(p1, DMEM[i])
elif cmd == 4: # CPY
if p1 == 0: DMEM[p3] = p2
elif p1 == 1: DMEM[p3] = v2
elif p1 == 2: DMEM[int(v3)] = p2
elif p1 == 3: DMEM[int(v3)] = v2
elif p1 == 4: DMEM[int(v3)] = DMEM[int(v2)]
elif cmd == 5: DMEM[p3] = p1 * 100 + p2 + v3 / 100.0 # SET
elif cmd == 6: # MAT
if p1 == 0: DMEM[p3] = v2 + v3
elif p1 == 1: DMEM[p3] = v2 - v3
elif p1 == 2: DMEM[p3] = v2 * v3
elif p1 == 3: DMEM[p3] = 0 if v3 == 0 else (v2 / v3)
elif p1 == 4: DMEM[p3] = math.floor(v2) if v3 == 0 else (v2 % v3)
elif p1 == 5: DMEM[p3] = math.fabs(v2)
elif p1 == 6: DMEM[p3] = math.sqrt(math.fabs(v2))
elif p1 == 7: DMEM[p3] = math.exp(v2)
elif p1 == 8: DMEM[p3] = 0 if v2 == 0 else math.log(math.fabs(v2))
elif p1 == 9: DMEM[p3] = math.sin(v2)
elif p1 == 10: DMEM[p3] = math.cos(v2)
elif p1 == 11: DMEM[p3] = math.atan(v2)
elif cmd == 7: DMEM[p3] = float(random.randint(int(v1), int(v2))) # RND
lno += 1 # increment the program counter
if lno >= MEMLIMIT: break
# interactive mode entry function
def intermode(cmd:int, p1:int, p2:int):
if cmd == 0 and p1 < MEMLIMIT: iexec(p1) # run from the step
elif cmd == 1 and p1 < MEMLIMIT: PMEM[p1] = p2 # enter the instruction into PMEM
elif cmd == 2 and p1 < MEMLIMIT and p2 < MEMLIMIT: # clear instructions
for cmd in range(p1, p2+1): PMEM[cmd] = 0
elif cmd == 3 and p1 < MEMLIMIT and p2 < MEMLIMIT: # clear data
for cmd in range(p1, p2+1): DMEM[cmd] = 0.0
elif cmd == 4:
print("Bye!")
sys.exit(0)
# main REPL environment
if __name__ == '__main__':
vreg = re.compile(r'[^-\d]+')
if len(sys.argv) > 1: # preload the file contents
fc = ''
try:
fd = open(sys.argv[1], 'r')
fc = fd.read()
close(fd)
except:
pass
instr = [] # current instruction cache
iindex = 0
for instrpart in vreg.split(fc):
if len(instrpart) > 0:
intermode(1, iindex, int(instrpart))
instr.append(int(instrpart))
iindex += 1
instr = [] # current instruction cache
while True: # main interactive loop
rinput = input('> ')
if len(rinput) > 0:
for v in vreg.split(rinput): # loop over the current input
if len(v) > 0:
instr.append(v)
if len(instr) == 3: # full instruction registered
intermode(int(instr[0]), int(instr[1]), int(instr[2]))
instr = []