#!/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 = []