/** * 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 #include #include #include /* POSIX-specific terminal stuff for unbuffered input for I/O port 2 */ #if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__)) #define NLC "\n" #include #include 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 */ DMEM[127] = 1; /* force the value at 127 to always be 1 */ 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 */ } }