114 lines
4.6 KiB
Awk
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("> ")
|
|
}
|
|
}
|