diff --git a/README.md b/README.md index 2945086..a5789dc 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/mu808.awk b/mu808.awk new file mode 100644 index 0000000..ca4c2ee --- /dev/null +++ b/mu808.awk @@ -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 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 ") + while(getline) { # main REPL + csize = split($0, icache) + for(i=0;i ") + } +} + diff --git a/mu808.c b/mu808.c index c68346b..885796f 100644 --- a/mu808.c +++ b/mu808.c @@ -10,9 +10,6 @@ #include #include #include -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#endif /* POSIX-specific terminal stuff for unbuffered input for I/O port 2 */ #if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__))