User:Marinus/Word!CPU interpreter
Jump to navigation
Jump to search
This is my Word!CPU interpreter (emulator?).
/*
* wordcpu.c
* by Marinus Oosters
*
* usage:
* wordcpu -p program [-m file] [-i file] [-I file] [-o file] [-O file]
*
* -p : initialize program memory
* -m : initialize data memory (default: zeroes)
* -i : input 1 (default: stdin)
* -o : output 1 (default: stdout)
* -I : input 2 (default: stderr)
* -O : output 2 (default: stderr)
* -d : debug on
*
* NOTE: I'm assuming the 8 registers and the 8 'pointers' are the same thing.
* The specs seem to mean this but it's not explicitly stated. The pointers wrap,
* the instruction pointer does NOT: jumping out of memory space ends the program.
*
* ALSO NOTE: I've tried to keep endianness out of it as much as possible,
* but this program has only been tested on a low-endian system.
*
* I added a Block D command, E, which ends the program/halts the CPU.
*
* 1110nnnn nnnnnnnn
*
* This is public domain software. Share and enjoy!
*
*
*/
#include <stdio.h>
#include <getopt.h>
#define ERRSTR "IP: %4x REGS: %2x %2x %2x %2x %2x %2x %2x %2x\n"\
"DATA MEM: %2x %2x %2x %2x %2x %2x %2x %2x\n"\
"PGM MEM: %2x %2x\n\n"
#define ERRVAR ip, reg[0],reg[1],reg[2],reg[3],reg[4],reg[5],reg[6],reg[7],\
dmem[reg[0]],dmem[reg[1]],dmem[reg[2]],dmem[reg[3]],\
dmem[reg[4]],dmem[reg[5]],dmem[reg[6]],dmem[reg[7]],\
pmem[ip], pmem[ip+1]
// memory
unsigned char dmem[0x2000], *dpt = dmem;
unsigned char pmem[0x2000], *ppt = pmem;
// registers & pointers
unsigned int reg[8] = {0,0,0,0,0,0,0,0};
int ip = 0, dp = 0;
int debug=0;
usage(){
printf(
"usage: wordcpu -p program [-d] [-m file] [-i file] [-I file] [-o file] [-O file]\n"
"\n"
" -p : initialize program memory\n"
" -m : initialize data memory (default: zeroes)\n"
" -i : input 1 (default: stdin)\n"
" -o : output 1 (default: stdout)\n"
" -I : input 2 (default: stderr)\n"
" -O : output 2 (default: stderr)\n"
" -d : debug on\n"
);
exit(0);
}
die(char*x){
fprintf(stderr,x);
exit(1);
}
unsigned char* lhb(int *b, char lh) {
int foo=40;
if ( ((char*)&foo)[0] != 40) lh = !lh;
lh = !!lh;
return ((unsigned char*)b)+lh;
}
/* split commands into respective fields */
/* all commands are two bytes long, and divided in four blocks:
byte 1 byte 2
A: 000VVVVV VVVVVVV0 (the last 0 is actually part of the V)
B: CCCCXPPP VVVVVVVV
C: 1111XPPP CCCCYRRR
D: CCCCYRRR VVVVVVVV
where:
V = address or value
C = command
X = switch between data and program memory (0=program, 1=data)
P = pointer
Y = switch between high and low bits (0=low, 1=high)
R = register
blocks:
0 = A; 1 = B; 2 = C; 3 = D
Return code:
0 = success
1 = block error
*/
char getcmd (unsigned char byte1, unsigned char byte2,
unsigned char *block, unsigned int *v,
unsigned char *x, unsigned char *p,
unsigned char *y, unsigned char *r,
unsigned char *c) {
// determine block
switch(byte1>>4) {
case 0: case 1: *block = 0; break;
case 2: case 3: case 4: case 5: case 6: case 7: case 8: *block = 1; break;
case 15: *block = 2; break;
case 9: case 10: case 11: case 14: *block = 3; break;
default: return 1;
}
switch(*block) {
case 0: *v = (((int)byte1)<<8) + (int)byte2; *x=*p=*y=*r=0; break;
case 1: *c = (byte1>>4);
*x = ((byte1%16)>>3);
*p = (byte1%8);
*v = byte2;
*y = *r = 0;
break;
case 2: *x = ((byte1%16)>>3);
*p = (byte1%8);
*c = (byte2>>4);
*y = ((byte2%16)>>3);
*r = (byte2%8);
*v = 0;
break;
case 3: *c = (byte1>>4);
*y = ((byte1%16)>>3);
*r = (byte1%8);
*v = byte2;
*x = *p = 0;
break;
default: return 1;
}
return 0;
}
int main(int argc, char **argv) {
int foo=0;
char p,m,i,o,I,O;
p=m=i=o=I=O=0;
FILE *pp,*mm,*ii,*oo,*II,*OO;
while((foo=getopt(argc,argv,"p:m:i:o:I:O:d"))!=-1){
switch(foo) {
case 'p':
p=1;if(!(pp=fopen(optarg,"r")))die("Cannot open program file\n");
break;
case 'm':
m=1;if(!(mm=fopen(optarg,"r")))die("Cannot open memory file\n");
break;
case 'i':
i=1;if(!(ii=fopen(optarg,"r")))die("Cannot open file for input 1\n");
break;
case 'o':
o=1;if(!(oo=fopen(optarg,"w")))die("Cannot open file for output 1\n");
break;
case 'I':
I=1;if(!(II=fopen(optarg,"r")))die("Cannot open file for input 2\n");
break;
case 'O':
O=1;if(!(OO=fopen(optarg,"W")))die("Cannot open file for output 2\n");
break;
case 'd':
debug=1;
break;
}
}
// zero memory
bzero(dmem,0x2000);
bzero(pmem,0x2000);
// read program file
if (!p) usage();
while(!feof(pp))*(ppt++)=fgetc(pp);ppt=pmem;
// read memory file
if (m) while (!feof(mm))*(dpt++)=fgetc(mm);dpt=dmem;
i||(ii=stdin);
o||(oo=stdout);
I||(II=stderr);
O||(OO=stderr);
// run
char block,c,x,y,p_,r;
int v;
ip = dp = foo = 0;
while(ip < 0x2000 && ip>-1) {
// get command:
if (getcmd(pmem[ip],pmem[ip+1],&block,&v,&x,&p_,&y,&r,&c)) {
fprintf(stderr, "ERROR: INVALID CMD: cannot determine block.\n" ERRSTR, ERRVAR);
return 0;
}
// 'debug'
if (debug) {
fprintf(stderr, "CMD: %2x %2x :: ip=%4x block=%1x v=%2x x=%2x p=%2x y=%2x r=%2x c=%2x\n"
"REG: %2x %2x %2x %2x %2x %2x %2x %2x\n"
"DAT: %2x %2x %2x %2x %2x %2x %2x %2x\n"
"PGM: %2x %2x %2x %2x %2x %2x %2x %2x\n",
pmem[ip]%256,pmem[ip+1]%256,ip,block,v,x,p_,y,r,c,
reg[0],reg[1],reg[2],reg[3],reg[4],reg[5],reg[6],reg[7],
dmem[reg[0]],dmem[reg[1]],dmem[reg[2]],dmem[reg[3]],dmem[reg[4]],
dmem[reg[5]],dmem[reg[6]],dmem[reg[7]],
pmem[reg[0]],pmem[reg[1]],pmem[reg[2]],pmem[reg[3]],pmem[reg[4]],
pmem[reg[5]],pmem[reg[6]],pmem[reg[7]]);
}
switch(block) {
case 0: /*A*/ ip = v-2; break; // the 2 will be added back at the end.
case 1: /*B*/
switch(c) {
case 2: (x?pmem:dmem)[reg[p_]] += v; break;
case 3: reg[p_] -= v; reg[p_] %= 0x2000; break;
case 4: reg[p_] += v; reg[p_] %= 0x2000; break;
case 5: (x?pmem:dmem)[reg[p_]] = ~((x?pmem:dmem)[reg[p_]] & (char)v); break;
case 6: if ((x?pmem:dmem)[reg[p_]]) ip -= v*2 + 2;break; /* 2 is added at the end of the loop */
case 7: if (!(x?pmem:dmem)[reg[p_]]) ip += v*2 - (!!v)*2;break; /* 2 is added at the end of the loop */
case 8:
switch(v) {
case 0: (x?pmem:dmem)[reg[p_]] = fgetc(ii); break;
case 1: (x?pmem:dmem)[reg[p_]] = fgetc(II); break;
case 2: fputc((x?pmem:dmem)[reg[p_]],oo); break;
case 3: fputc((x?pmem:dmem)[reg[p_]],OO); break;
default:
fprintf(stderr, "ERROR: INVALID CMD: I/O mode %2x unknown.\n" ERRSTR,
v, ERRVAR);
return 0;
}
break;
default:
fprintf(stderr, "ERROR: INVALID CMD: No cmd %2x in block %c.\n" ERRSTR,
c, (block==0)?'A':(block==1)?'B':(block==2)?'C':(block==3)?'D':'!',
ERRVAR);
return 0;
}
break;
case 2: /*C*/
switch(c) {
case 0: (x?pmem:dmem)[reg[p_]] = *(lhb(®[r],y)); break;
case 1: *(lhb(®[r],y)) = (x?pmem:dmem)[reg[p_]]; break;
case 2: foo=(x?pmem:dmem)[reg[p_]];(x?pmem:dmem)[reg[p_]]=*(lhb(®[r],y));*(lhb(®[r],y))=foo;break;
case 3: *(lhb(®[r],y)) = ~(*(lhb(®[r],y)) & (x?pmem:dmem)[reg[p_]]); break;
case 4: (x?pmem:dmem)[reg[p_]] = ~((x?pmem:dmem)[reg[p_]] & *(lhb(®[r],y))); break;
default:
fprintf(stderr, "ERROR: INVALID CMD: No cmd %2x in block %c.\n" ERRSTR,
c, (block==0)?'A':(block==1)?'B':(block==2)?'C':(block==3)?'D':'!',
ERRVAR);
return 0;
}
break;
case 3: /*D*/
switch(c){
case 9: *(lhb(®[r],y)) = v; break;
case 10: if (!*(lhb(®[r],y))) ip += v*2 + (!!v)*2; break;
case 11: if (*(lhb(®[r],y))) ip -= v*2 - 2; break;
case 14: return 0;
default:
fprintf(stderr, "ERROR: INVALID CMD: No cmd %2x in block %c.\n" ERRSTR,
c, (block==0)?'A':(block==1)?'B':(block==2)?'C':(block==3)?'D':'!',
ERRVAR);
return 0;
}
}
ip += 2;
}
return 0;
}