added POSIX AWK reference implementation

This commit is contained in:
Luxferre
2025-04-23 15:05:00 +03:00
parent 5577146162
commit db8422a1fd
3 changed files with 172 additions and 3 deletions
+8
View File
@@ -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 If you just want to test an implementation, assembled MU8 machine code files
(in the plaintext format) are stored in the `examples/assembled` subdirectory. (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 Reference implementations
------------------------- -------------------------
@@ -274,6 +276,12 @@ Reference implementations
`fma()` call that appeared in the C99 standard. Accepts a file name to preload `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: a MU8 machine code program from the OS command line. Compile the source with:
`cc -std=c99 -O2 -s -lm -o mu808 mu808.c` `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 ### Assembler reference implementations
+164
View File
@@ -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("> ")
}
}
-3
View File
@@ -10,9 +10,6 @@
#include <stdio.h> #include <stdio.h>
#include <time.h> #include <time.h>
#include <math.h> #include <math.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
/* POSIX-specific terminal stuff for unbuffered input for I/O port 2 */ /* POSIX-specific terminal stuff for unbuffered input for I/O port 2 */
#if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__)) #if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__))