#!/usr/bin/env awk -f # n808 VM reference implementation in POSIX AWK # Run with: LC_ALL=C awk -f n808.awk [- input_program.n8] # Supports the entire n808 spec except the I/O port 3 # See the README.md file for all documentation # Created by Luxferre in 2025, released into public domain # absolute value function function fabs(v) {return (v < 0) ? -v : v} # port I/O function (all ports supported except 3) function portio(port, data) { if(port == 0) printf("%f\n", data) # standard numeric output else if(port == 1) getline data # standard numeric input else if(port == 2) printf("%c", int(data) % 256) # character output return +data } # main instruction execution logic function iexec(lno, halt, data_override, cmd, p1, p2, p3, i, v1, v2, v3) { halt = data_override = 0 while(halt == 0) { # decode and execute the current instruction cmd = int(PMEM[lno] / 2097152) % 8 # command opcode p1 = (data_override ? int(v1) : int(PMEM[lno] / 16384)) % 128 # parameter 1 p2 = (data_override ? int(v2) : int(PMEM[lno] / 128)) % 128 # parameter 2 p3 = (data_override ? int(v3) : PMEM[lno]) % 128 # 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 && p1 == 0) || (v2 > 0 && p1 == 1) || (v2 < 0 && p1 == 2) \ || (v2 >= 0 && p1 == 3) || (v2 <= 0 && p1 == 4) || (v2 != 0 && p1 == 5) \ || p1 == 6 || p1 == 14) lno = p3 - 1 else if((v2 == 0 && p1 == 7) || (v2 > 0 && p1 == 8) || (v2 < 0 && p1 == 9) \ || (v2 >= 0 && p1 == 10) || (v2 <= 0 && p1 == 11) || (v2 != 0 && p1 == 12) \ || p1 == 13) lno = int(v3) - 1 } else if(cmd == 2) data_override = 1 # IAT else if(cmd == 3) for(i=p2;i<=p3;i++) DMEM[i] = portio(p1, DMEM[i]) # INO else if(cmd == 4) { # CPY if(p1 == 0) DMEM[p3] = p2 else if(p1 == 1) DMEM[p3] = v2 else if(p1 == 2) DMEM[int(v3)] = p2 else if(p1 == 3) DMEM[int(v3)] = v2 else if(p1 == 4) DMEM[int(v3)] = DMEM[int(v2)] } else if(cmd == 5) DMEM[p3] = p1 * 100 + p2 + v3 / 100.0 # SET else if(cmd == 6) { # MAT if(p1 == 0) DMEM[p3] = v2 + v3 else if(p1 == 1) DMEM[p3] = v2 - v3 else if(p1 == 2) DMEM[p3] = v2 * v3 else if(p1 == 3) DMEM[p3] = (v3 == 0) ? 0 : (v2 / v3) else if(p1 == 4) DMEM[p3] = (v3 == 0) ? int(v2) : (v2 % v3) else if(p1 == 5) DMEM[p3] = fabs(v2) else if(p1 == 6) DMEM[p3] = sqrt(fabs(v2)) else if(p1 == 7) DMEM[p3] = exp(v2) else if(p1 == 8) DMEM[p3] = (v2 == 0) ? 0 : log(fabs(v2)) else if(p1 == 9) DMEM[p3] = sin(v2) else if(p1 == 10) DMEM[p3] = cos(v2) else if(p1 == 11) DMEM[p3] = atan(v2) } else if(cmd == 7) # RND DMEM[p3] = int(v1) + int(rand() * (int(v2) - int(v1) + 1)) lno++ # increment the program counter if(lno >= MEMLIMIT) halt = 1 } } # interactive mode entry function function intermode(cmd, p1, p2) { if(cmd == 0 && p1 < MEMLIMIT) iexec(p1) # run from the step else if(cmd == 1) PMEM[p1] = p2 # enter the instruction into PMEM else if(cmd == 2 && p1 < MEMLIMIT && p2 < MEMLIMIT) # clear instructions for(cmd=p1;cmd<=p2;cmd++) PMEM[cmd] = 0 else if(cmd == 3 && p1 < MEMLIMIT && p2 < MEMLIMIT) # clear data for(cmd=p1;cmd<=p2;cmd++) DMEM[cmd] = 0.0 else if(cmd == 4) {print("Bye!"); exit(0)} } BEGIN { # VM entry point MEMLIMIT = 128 for(i=0;i 1) { # preload the input program fname = ARGV[ARGC-1] iindex = 0 while(getline < fname) { # iterate over the file lines csize = split($0, icache) for(i=0;i ") iindex = cmd = p1 = p2 = 0 while(getline) { # main REPL csize = split($0, icache) for(i=0;i ") } }