Added assembled examples and a C99 reference implementation
This commit is contained in:
@@ -254,6 +254,9 @@ repository, or even by hand (by numbering lines, resolving the labels and
|
||||
replacing mnemonics with corresponding opcodes). The "Hellorld!" example only
|
||||
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.
|
||||
|
||||
Reference implementations
|
||||
-------------------------
|
||||
|
||||
@@ -266,6 +269,11 @@ Reference implementations
|
||||
character input port correctly on desktop OSes but falls back to a simple
|
||||
stream file read in case the library is not found). Accepts a file name to
|
||||
preload a MU8 (plain text) machine code program from the OS command line.
|
||||
* [ISO C99 implementation](mu808.c): supports the entire specification the
|
||||
same way as the Python implementation, and also makes use of the optimized
|
||||
`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`
|
||||
|
||||
### Assembler reference implementations
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
1 5 100 0 1
|
||||
2 4 2 3 0
|
||||
3 9 2 1 4
|
||||
4 5 1 0 5
|
||||
5 7 5 4 5
|
||||
6 13 1 5 5
|
||||
7 7 0 3 5
|
||||
8 13 0 5 5
|
||||
9 3 5 5 0
|
||||
@@ -0,0 +1,19 @@
|
||||
1 5 72 0 1
|
||||
2 5 101 0 2
|
||||
3 5 108 0 3
|
||||
4 5 108 0 4
|
||||
5 5 111 0 5
|
||||
6 5 114 0 6
|
||||
7 5 108 0 7
|
||||
8 5 100 0 8
|
||||
9 5 33 0 9
|
||||
10 5 10 0 10
|
||||
11 5 1 0 50
|
||||
12 5 1 0 10
|
||||
13 5 10 0 11
|
||||
14 5 12 0 12
|
||||
15 6 2 10 12
|
||||
16 3 12 12 1
|
||||
17 8 11 50 11
|
||||
18 7 50 50 10
|
||||
19 1 1 11 14
|
||||
@@ -0,0 +1,49 @@
|
||||
1 5 0 0 10
|
||||
2 5 0 0 11
|
||||
3 5 0 0 12
|
||||
4 5 0 0 13
|
||||
5 5 0 0 14
|
||||
6 5 0 0 15
|
||||
7 5 1 0 50
|
||||
8 4 1 2 0
|
||||
9 6 1 1 3
|
||||
10 7 0 3 3
|
||||
11 6 1 2 4
|
||||
12 7 0 4 4
|
||||
13 6 1 2 5
|
||||
14 7 0 1 5
|
||||
15 7 1 50 10
|
||||
16 7 2 50 11
|
||||
17 7 3 50 12
|
||||
18 7 4 50 13
|
||||
19 7 5 50 14
|
||||
20 7 50 50 15
|
||||
21 6 1 3 6
|
||||
22 7 4 50 6
|
||||
23 1 1 6 8
|
||||
24 8 15 50 15
|
||||
25 6 1 15 1
|
||||
26 7 0 14 1
|
||||
27 6 1 11 5
|
||||
28 7 0 10 5
|
||||
29 8 1 5 6
|
||||
30 6 1 15 3
|
||||
31 7 0 12 3
|
||||
32 6 1 10 4
|
||||
33 7 0 10 4
|
||||
34 8 3 4 3
|
||||
35 9 6 3 21
|
||||
36 6 1 10 1
|
||||
37 7 0 21 1
|
||||
38 8 11 1 1
|
||||
39 9 1 15 20
|
||||
40 6 1 11 2
|
||||
41 7 0 11 2
|
||||
42 6 1 15 5
|
||||
43 7 0 13 5
|
||||
44 8 5 2 2
|
||||
45 6 1 3 1
|
||||
46 7 0 2 1
|
||||
47 12 0 1 1
|
||||
48 9 6 1 22
|
||||
49 3 20 22 0
|
||||
@@ -0,0 +1,63 @@
|
||||
1 5 1 0 50
|
||||
2 5 10 0 52
|
||||
3 5 16 2500 53
|
||||
4 5 7480 0 54
|
||||
5 5 3 4483 55
|
||||
6 5 2900 0 56
|
||||
7 5 1930 0 40
|
||||
8 5 100 0 57
|
||||
9 7 0 57 40
|
||||
10 5 1609 0 41
|
||||
11 5 7260 0 42
|
||||
12 5 4444 0 30
|
||||
13 5 5555 0 31
|
||||
14 5 6666 0 32
|
||||
15 5 7777 0 33
|
||||
16 5 8888 0 34
|
||||
17 5 26 6667 0
|
||||
18 5 9 7300 0
|
||||
19 5 4 4445 0
|
||||
20 5 0 4500 0
|
||||
21 3 40 42 0
|
||||
22 5 0 0 2
|
||||
23 1 4 42 28
|
||||
24 4 1 1 0
|
||||
25 6 1 1 2
|
||||
26 7 0 55 2
|
||||
27 6 1 42 3
|
||||
28 7 54 50 3
|
||||
29 8 3 2 4
|
||||
30 9 3 4 1
|
||||
31 13 1 1 1
|
||||
32 7 0 56 1
|
||||
33 8 41 1 41
|
||||
34 7 53 50 41
|
||||
35 6 1 41 3
|
||||
36 7 0 52 3
|
||||
37 8 40 3 40
|
||||
38 8 42 2 42
|
||||
39 1 1 42 41
|
||||
40 5 0 0 42
|
||||
41 1 1 40 43
|
||||
42 5 0 0 40
|
||||
43 8 40 52 1
|
||||
44 1 1 1 21
|
||||
45 3 40 42 0
|
||||
46 8 41 39 1
|
||||
47 1 1 1 50
|
||||
48 3 34 34 0
|
||||
49 1 6 0 63
|
||||
50 8 41 38 1
|
||||
51 1 1 1 54
|
||||
52 3 33 33 0
|
||||
53 1 6 0 63
|
||||
54 8 41 37 1
|
||||
55 1 1 1 58
|
||||
56 3 32 32 0
|
||||
57 1 6 0 63
|
||||
58 8 41 36 1
|
||||
59 1 1 1 62
|
||||
60 3 31 31 0
|
||||
61 1 6 0 63
|
||||
62 3 30 30 0
|
||||
63 0 0 0 0
|
||||
@@ -0,0 +1,60 @@
|
||||
1 5 0 0 60
|
||||
2 5 1 0 61
|
||||
3 5 2 0 62
|
||||
4 5 3 0 63
|
||||
5 5 4 0 64
|
||||
6 5 5 0 65
|
||||
7 5 6 0 66
|
||||
8 5 7 0 67
|
||||
9 5 8 0 68
|
||||
10 5 9 0 69
|
||||
11 5 1 0 50
|
||||
12 5 4 0 11
|
||||
13 5 60 0 70
|
||||
14 5 80 0 71
|
||||
15 5 10 0 3
|
||||
16 5 9 0 1
|
||||
17 15 0 1 1
|
||||
18 7 70 50 1
|
||||
19 5 2 0 2
|
||||
20 6 2 1 2
|
||||
21 8 2 3 4
|
||||
22 1 0 4 16
|
||||
23 2 0 3 1
|
||||
24 6 0 0 0
|
||||
25 6 1 11 1
|
||||
26 7 71 50 1
|
||||
27 5 2 0 5
|
||||
28 6 2 5 1
|
||||
29 8 11 50 11
|
||||
30 1 1 11 16
|
||||
31 5 0 1000 51
|
||||
32 5 7 0 11
|
||||
33 4 91 94 0
|
||||
34 5 90 0 70
|
||||
35 5 0 0 20
|
||||
36 5 4 0 21
|
||||
37 5 4 0 22
|
||||
38 6 1 21 15
|
||||
39 7 71 50 15
|
||||
40 6 1 22 16
|
||||
41 7 70 50 16
|
||||
42 2 15 16 50
|
||||
43 8 0 0 0
|
||||
44 1 5 1 50
|
||||
45 8 21 22 2
|
||||
46 1 5 2 49
|
||||
47 7 50 50 20
|
||||
48 1 6 0 50
|
||||
49 7 51 50 20
|
||||
50 8 22 50 22
|
||||
51 1 1 22 38
|
||||
52 8 21 50 21
|
||||
53 1 1 21 37
|
||||
54 3 20 20 0
|
||||
55 5 4 0 7
|
||||
56 8 20 7 1
|
||||
57 1 0 1 60
|
||||
58 8 11 50 11
|
||||
59 1 1 11 33
|
||||
60 3 81 84 0
|
||||
@@ -0,0 +1,195 @@
|
||||
/**
|
||||
* 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 */
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user