added POSIX AWK reference implementation
This commit is contained in:
@@ -256,6 +256,8 @@ works on the platforms supporting I/O port 1 (character output).
|
||||
|
||||
If you just want to test an implementation, assembled MU8 machine code files
|
||||
(in the plaintext format) are stored in the `examples/assembled` subdirectory.
|
||||
After loading into the REPL, you can run each of them with the `0 1 0 0 1` or
|
||||
`0 1 6 0 1` sequence.
|
||||
|
||||
Reference implementations
|
||||
-------------------------
|
||||
@@ -274,6 +276,12 @@ Reference implementations
|
||||
`fma()` call that appeared in the C99 standard. Accepts a file name to preload
|
||||
a MU8 machine code program from the OS command line. Compile the source with:
|
||||
`cc -std=c99 -O2 -s -lm -o mu808 mu808.c`
|
||||
* [POSIX AWK implementation](mu808.awk): supports the entire mu808 specification
|
||||
sans the (non-portable) I/O port 2. Run it like this:
|
||||
`LC_ALL=C awk -f mu808.awk [- input_program.mu8]`, where the dash-delimited
|
||||
program file name is optional and used for preloading MU8 program files into
|
||||
VM's memory. The Busybox AWK implementation is compact but quite slow, so use
|
||||
it as a last resort solution when no other programming environment is available.
|
||||
|
||||
### Assembler reference implementations
|
||||
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
#!/usr/bin/env awk -f
|
||||
# mu808 VM reference implementation in POSIX AWK
|
||||
# Run with: LC_ALL=C awk -f mu808.awk [- input_program.mu8]
|
||||
# Supports the entire mu808 spec except the I/O port 2
|
||||
# See the README.md file for all documentation
|
||||
# Created by Luxferre in 2025, released into public domain
|
||||
|
||||
# port output function
|
||||
function portout(port, data) {
|
||||
if(port == 0) printf("%f\n", data) # standard numeric output
|
||||
else if(port == 1) printf("%c", int(data) % 256) # character output
|
||||
}
|
||||
|
||||
# port input function
|
||||
function portin(port, val) {
|
||||
val = 0
|
||||
if(port == 0) getline val # standard numeric input port
|
||||
return +val
|
||||
}
|
||||
|
||||
# absolute value function
|
||||
function fabs(v) {return (v < 0) ? -v : v}
|
||||
|
||||
# instruction line execution function (the main mu808 logic is defined here)
|
||||
function ilexec(lno, cmd, x, y, z, halt, data_override, bcheck, i, v1, v2, v3) {
|
||||
halt = 0
|
||||
while(halt == 0) {
|
||||
if(traceflag) printf("PC: %hu INSTR: %hu %hu %hu %hu\n", lno, cmd, x, y, z)
|
||||
DMEM[0] = data_override = 0 # force the value at 0 to be always 0
|
||||
bcheck = (x < MEMLIMIT) && (y < MEMLIMIT) && (z < MEMLIMIT)
|
||||
if(bcheck) {
|
||||
v1 = DMEM[x]; v2 = DMEM[y]; v3 = DMEM[z] # prefetch the memory values
|
||||
if(cmd == 1) { # JMP
|
||||
if((v2 == 0 && x == 0) || (v2 > 0 && x == 1) || (v2 < 0 && x == 2) \
|
||||
|| (v2 >= 0 && x == 3) || (v2 <= 0 && x == 4) || (v2 != 0 && x == 5) \
|
||||
|| x == 6) { halt = 0; lno = z - 1 }
|
||||
else if((v2 == 0 && x == 7) || (v2 > 0 && x == 8) || (v2 < 0 && x == 9) \
|
||||
|| (v2 >= 0 && x == 10) || (v2 <= 0 && x == 11) || (v2 != 0 && x == 12) \
|
||||
|| x == 13) { halt = 0; lno = int(v3) - 1 }
|
||||
} else if(cmd == 2) data_override = 1 # IAT
|
||||
else if(cmd == 3) for(i=x;i<=y;i++) portout(z, DMEM[i]) # OUT
|
||||
else if(cmd == 4) for(i=x;i<=y;i++) DMEM[i] = portin(z) # INP
|
||||
else if(cmd == 5) DMEM[z] = (x % 10000) + (y % 10000) / 10000.0 # SET
|
||||
else if(cmd == 6) { # CPY
|
||||
if(x == 0) DMEM[z] = y
|
||||
else if(x == 1) DMEM[z] = v2
|
||||
else if(v2 < MEMLIMIT && v3 < MEMLIMIT)
|
||||
DMEM[int(v3)] = DMEM[int(v2)]
|
||||
} else if(cmd == 7) DMEM[z] = v1 + v2 * v3 # FMA
|
||||
else if(cmd == 8) DMEM[z] = v1 - v2 # SUB
|
||||
else if(cmd == 9) DMEM[z] = (v2 == 0) ? 0 : (v1 / v2) # DIV
|
||||
else if(cmd == 10) DMEM[z] = (v2 == 0) ? int(v1) : (v1 % v2) # MDF
|
||||
else if(cmd == 11) DMEM[z] = fabs(v2) # ABS
|
||||
else if(cmd == 12) DMEM[z] = sqrt(fabs(v2)) # SQR
|
||||
else if(cmd == 13) { # NEL
|
||||
if(x == 0) DMEM[z] = exp(v2)
|
||||
else DMEM[z] = (v2 == 0) ? 0 : log(fabs(v2))
|
||||
} else if(cmd == 14) { # TRI
|
||||
if(x == 0) DMEM[z] = sin(v2)
|
||||
else if(x == 1) DMEM[z] = cos(v2)
|
||||
else DMEM[z] = atan2(v2, 1)
|
||||
} else if(cmd == 15) { # RND
|
||||
v1 = int(v1)
|
||||
DMEM[z] = v1 + int(rand() * (int(v2) - v1 + 1))
|
||||
}
|
||||
}
|
||||
lno++ # increment the program counter
|
||||
if(lno >= MEMLIMIT || lno < 1 || (runlimit > 0 && runcount > runlimit)) {
|
||||
halt = 1
|
||||
if(traceflag) print("Memory limit or runlimit hit, halting...")
|
||||
} else { # fetch the next instruction
|
||||
runcount++
|
||||
if(data_override && bcheck) {
|
||||
cmd = PMEM[lno * 4]
|
||||
x = int(v1) % MEMLIMIT
|
||||
y = int(v2) % MEMLIMIT
|
||||
z = int(v3) % MEMLIMIT
|
||||
} else {
|
||||
cmd = PMEM[lno * 4]
|
||||
x = PMEM[lno * 4 + 1]
|
||||
y = PMEM[lno * 4 + 2]
|
||||
z = PMEM[lno * 4 + 3]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# instruction line entry function (interactive mode)
|
||||
function ilenter(lno, cmd, x, y, z) {
|
||||
if(lno > 0) { # record the instruction in memory
|
||||
PMEM[lno * 4] = cmd
|
||||
PMEM[lno * 4 + 1] = x
|
||||
PMEM[lno * 4 + 2] = y
|
||||
PMEM[lno * 4 + 3] = z
|
||||
} else if(lno == 0) { # immediate execution
|
||||
runcount = 0
|
||||
ilexec(lno, cmd, x, y, z)
|
||||
} else if(lno == -1) { # display a range of instructions from cmd to x
|
||||
if(cmd < MEMLIMIT && x < MEMLIMIT) for(y=cmd;y<=x;y++) {
|
||||
z = y * 4
|
||||
printf("@%hu:\t%hu %hu %hu %hu\n", y, PMEM[z], PMEM[z+1], PMEM[z+2], PMEM[z+3])
|
||||
}
|
||||
} else if(lno == -2) { # clear a range of data or instructions from x to y
|
||||
if(cmd < MEMLIMIT && x < MEMLIMIT) for(z=x;z<=y;z++) {
|
||||
if(cmd == 0)
|
||||
PMEM[z*4] = PMEM[z*4+1] = PMEM[z*4+2] = PMEM[z*4+3] = 0
|
||||
else DMEM[z] = 0.0
|
||||
}
|
||||
} else if(lno == -3) { # tracing on/off
|
||||
if(cmd == 0) {traceflag = 0; print("Tracing off")}
|
||||
else {traceflag = 1; print("Tracing on")}
|
||||
} else if(lno == -4) { # set runlimit
|
||||
printf("Runlimit set to %u\n", runlimit = cmd)
|
||||
} else if(lno == -5) {print("Bye!"); exit(0)} # exit to the environment
|
||||
}
|
||||
|
||||
BEGIN { # VM entry point and main program REPL
|
||||
runlimit = MEMLIMIT = 16384
|
||||
traceflag = runcount = 0
|
||||
for(i=0;i<MEMLIMIT;i++) # initialize data and program memory arrays
|
||||
DMEM[i] = PMEM[i*4] = PMEM[i*4 + 1] = PMEM[i*4 + 2] = PMEM[i*4 + 3] = 0
|
||||
srand()
|
||||
if(ARGC > 1) { # preload the input program
|
||||
fname = ARGV[ARGC-1]
|
||||
iindex = lno = cmd = x = y = z = 0
|
||||
while(getline < fname) { # iterate over the file lines
|
||||
csize = split($0, icache)
|
||||
for(i=0;i<csize;i++) {
|
||||
a = int(icache[i+1])
|
||||
if(iindex == 0) lno = a
|
||||
else if(iindex == 1) cmd = a
|
||||
else if(iindex == 2) x = a
|
||||
else if(iindex == 3) y = a
|
||||
else if(iindex == 4) z = a
|
||||
iindex++
|
||||
if(iindex == 5) { # pefrorm the entry
|
||||
ilenter(lno, cmd, x, y, z)
|
||||
iindex = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
close(fname)
|
||||
}
|
||||
iindex = lno = cmd = x = y = z = 0
|
||||
printf("> ")
|
||||
while(getline) { # main REPL
|
||||
csize = split($0, icache)
|
||||
for(i=0;i<csize;i++) {
|
||||
a = int(icache[i+1])
|
||||
if(iindex == 0) lno = a
|
||||
else if(iindex == 1) cmd = a
|
||||
else if(iindex == 2) x = a
|
||||
else if(iindex == 3) y = a
|
||||
else if(iindex == 4) z = a
|
||||
iindex++
|
||||
if(iindex == 5) { # pefrorm the entry
|
||||
ilenter(lno, cmd, x, y, z)
|
||||
iindex = 0
|
||||
}
|
||||
}
|
||||
printf("> ")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user