Files
mu808/mu808.c
T

196 lines
7.4 KiB
C

/**
* mu808: an ultralight numeric-only VM
* ANSI C99 reference implementation
* Compile with: cc -std=c99 -O2 -s -lm -o mu808 mu808.c
* See the README.md file for all documentation
* Created by Luxferre in 2025, released into public domain
*/
#include <stdlib.h>
#include <stdio.h>
#include <time.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 */
#if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__))
#define NLC "\n"
#include <unistd.h>
#include <termios.h>
struct termios orig_termios;
void disable_raw_mode() {
tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig_termios);
}
void enable_raw_mode() {
tcgetattr(STDIN_FILENO, &orig_termios);
atexit(disable_raw_mode);
struct termios raw = orig_termios;
raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
raw.c_oflag &= ~(OPOST);
raw.c_cflag |= (CS8);
raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
}
#else
#define NLC "\r\n"
void disable_raw_mode() {}
void enable_raw_mode() {}
#endif
/* core memory */
#ifndef MEMLIMIT
#define MEMLIMIT 16384
#endif
#define ushort unsigned short
ushort PMEM[MEMLIMIT*4] = {0}; /* program memory */
double DMEM[MEMLIMIT] = {0.0}; /* data memory */
static unsigned int runlimit = MEMLIMIT;
static unsigned char traceflag = 0;
static unsigned int runcount = 0;
/* port output function */
void portout(ushort port, double data) {
if(port == 0) /* standard numeric output port */
printf("%f" NLC, data);
else if(port == 1) /* character output port */
fputc(((int)floor(data)) & 255, stdout);
}
/* port input function */
double portin(ushort port) {
double val = 0.0;
if(port == 0) /* standard numeric input port */
scanf("%lf", &val);
else if(port == 2) { /* character input port */
enable_raw_mode();
val = (double) (fgetc(stdin) & 255);
disable_raw_mode();
}
return val;
}
/* instruction line execution function (the main mu808 logic is defined here) */
void ilexec(ushort lno, ushort cmd, ushort x, ushort y, ushort z) {
unsigned char halt = 0, data_override = 0, bcheck;
ushort i;
double v1, v2, v3;
while(halt == 0) {
if(traceflag) printf("PC: %hu INSTR: %hu %hu %hu %hu" NLC, 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)floor(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)floor(v3)] = DMEM[(int)floor(v2)];
} else if(cmd == 7) DMEM[z] = fma(v3, v2, v1); /* 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) ? floor(v1) : fmod(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] = atan(v2);
} else if(cmd == 15) { /* RND */
v1 = floor(v1);
DMEM[z] = v1 + (rand() % (int)(floor(v2) + 1 - v1));
}
}
lno++; /* increment the program counter */
if(lno >= MEMLIMIT || lno < 1 || (runlimit > 0 && runcount > runlimit)) {
halt = 1;
if(traceflag) puts("Memory limit or runlimit hit, halting...");
} else { /* fetch the next instruction */
runcount++;
if(data_override && bcheck) {
cmd = PMEM[lno << 2];
x = ((int) floor(v1)) % MEMLIMIT;
y = ((int) floor(v2)) % MEMLIMIT;
z = ((int) floor(v3)) % MEMLIMIT;
} else {
cmd = PMEM[lno << 2];
x = PMEM[(lno << 2) + 1];
y = PMEM[(lno << 2) + 2];
z = PMEM[(lno << 2) + 3];
}
}
}
}
/* instruction line entry function */
void ilenter(int lno, ushort cmd, ushort x, ushort y, ushort z) {
if(lno > 0) { /* record the instruction in memory */
PMEM[lno << 2] = cmd;
PMEM[(lno << 2) + 1] = x;
PMEM[(lno << 2) + 2] = y;
PMEM[(lno << 2) + 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 << 2;
printf("@%hu:\t%hu %hu %hu %hu" NLC,
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<<2] = PMEM[(z<<2)+1] = PMEM[(z<<2)+2] = PMEM[(z<<2)+3] = 0;
else DMEM[z] = 0.0;
}
} else if(lno == -3) { /* tracing on/off */
if(cmd == 0) {traceflag = 0; puts("Tracing off");}
else {traceflag = 1; puts("Tracing on");}
} else if(lno == -4) {
printf("Runlimit set to %u" NLC, runlimit = cmd);
} else if(lno == -5) {puts("Bye!"); exit(0);}
}
/* entry point to the VM REPL */
void main(int argc, char* argv[]) {
srand(time(NULL));
printf("mu808 v1 by Luxferre" NLC "PROGMEM: %u steps" NLC "DATAMEM: %u floats" NLC,
MEMLIMIT, MEMLIMIT);
int lno; /* line number can be negative at this point */
ushort cmd, x, y, z; /* commands and their parameters */
if(argc > 1) { /* preload the file from a command line parameter */
FILE *fd = fopen(argv[1], "r");
if(fd != NULL) { /* opened successfully */
while(!feof(fd)) {
fscanf(fd, "%d %hu %hu %hu %hu", &lno, &cmd, &x, &y, &z); /* read the five numbers */
ilenter(lno, cmd, x, y, z); /* run the entry routine */
}
fclose(fd);
} else puts("Warning: no file could be preloaded!");
}
while(1) { /* main interactive loop */
printf("> ");
lno = cmd = x = y = z = 0;
scanf("%d %hu %hu %hu %hu", &lno, &cmd, &x, &y, &z); /* read the five numbers */
while(getchar() != '\n'); /* ignore the rest of the line */
ilenter(lno, cmd, x, y, z); /* run the entry routine */
}
}