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; }