Files
2025-05-04 11:46:54 +03:00

114 lines
4.6 KiB
Awk

#!/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<MEMLIMIT;i++) DMEM[i] = PMEM[i] = 0 # initialize both areas
srand()
if(ARGC > 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<csize;i++) {
a = int(icache[i+1])
intermode(1, iindex, a) # enter nth instruction
iindex++
}
}
close(fname)
}
printf("> ")
iindex = cmd = p1 = p2 = 0
while(getline) { # main REPL
csize = split($0, icache)
for(i=0;i<csize;i++) {
a = int(icache[i+1])
if(iindex == 0) cmd = a
else if(iindex == 1) p1 = a
else if(iindex == 2) p2 = a
iindex++
if(iindex == 3) { # pefrorm the entry
intermode(cmd, p1, p2)
iindex = 0
}
}
printf("> ")
}
}