Hi,
I tried to implement the Universal Machine as described in
http://www.boundvariable.org/task.shtml, and I managed to get one
implemented (After looking at what other's have done.) But when I use
to run a UM program, I kept on getting error messages. I have used
someone else's implementation and it runs fine. I have compared my
code with other's and I still can't figure it out what's wrong with
mine. So please help me out, after 3 days of debugging I don't think I
can ever make it. Here's my implementation. Thanks in advance.
#include <stdio.h> //printf(), putchar(), fprintf(), fread(),
fputchar(), strerror()
#include <string.h> //memcpy, memset
#include <stdlib.h> //malloc()
#include <errno.h> //errno
#include <sys/stat.h> //stat()
//internal data
typedef unsigned int u32;
typedef unsigned char u8;
//debug macros
#define DUMP_FILE_NAME "memory.dmp"
//#define PRINT_DEBUG_MSG
//#define DUMP_MEMORY
#ifdef PRINT_DEBUG_MSG
#define dprintf(x,...) printf(x,...)
#else
#define dprintf(x,...)
#endif
//decode macros
#define REG_A(codex) ((codex >6) & 7)
#define REG_B(codex) ((codex >3) & 7)
#define REG_C(codex) (codex & 7)
#define OPCODE(codex) ((codex >28) & 15)
#define SPEC_A(codex) ((codex >25) & 7)
#define SPEC_V(codex) (codex & 0x0fffffffL)
//opcodes
#define OP_CMOV 0
#define OP_INDEX 1
#define OP_AMAND 2
#define OP_ADD 3
#define OP_MUL 4
#define OP_DIV 5
#define OP_NAND 6
#define OP_HALT 7
#define OP_ALLOC 8
#define OP_FREE 9
#define OP_OUT 10
#define OP_IN 11
#define OP_LOAD 12
#define OP_ORTHO 13
//32 bit Universal Machine cpu struct
typedef struct UM32_{
u32 finger;
u32 r[8];
u32* program;
}UM32;
/*Change local small-endian to UM's internal big-endian
* ###require more work for portability
* this is to replace the inet/arpa.h's ntohl()
* ###
*/
void change_endian(u32* value){
u8 tmp1[4], tmp2[4];
memcpy((u32 *)tmp1, value, sizeof(u32));
tmp2[0] = tmp1[3];
tmp2[1] = tmp1[2];
tmp2[2] = tmp1[1];
tmp2[3] = tmp1[0];
memcpy(value, (u32 *)tmp2, sizeof(u32));
}
/* A wrapper for malloc() in stdlib.h */
void *my_malloc(size_t size){
void *tmp = malloc(size);
if(tmp != NULL)
return tmp;
else{
fprintf(stderr, "Falied in my_malloc() in attempt to allocate memory
of size: %d.\n",
size);
exit(1);
}
}
/* Create an array with size size+1,
* where array[-1] is used for holding the size of the array
* return a 32 bit arrayID (currently implemented as it's address)
*/
u32 create_array(u32 size){
u32 *tmp = (u32*)my_malloc(sizeof(u32)*size+1);
memset(tmp+1, 0, sizeof(u32)*size);
*tmp = size;
return (u32)(tmp+1);
}
#define GET_ARRAY_SIZE(arrayID) (*((u32*)(arrayID-1)))
#define ARRAY(arrayID) ((u32*)(arrayID))
#define WHOLE_ARRAY(arrayID) ((u32*)(arrayID)-1)
/* Delete an array created with create_array()*/
void delete_array(u32 arrayID){
free(WHOLE_ARRAY(arrayID));
}
/* Duplicate the content in from to that of to.
* original data in to is discarded.
*/
void copy2array0(UM32 *um, u32 src){
int size = (u32)GET_ARRAY_SIZE(src);
if(!(um->program))
free(um->program);
um->program = (u32*)my_malloc(sizeof(u32)*size+1);
memcpy(um->program, WHOLE_ARRAY(src), size+1);
um->program++;
}
void load_program(u32** membuffer, const char* filename){
int i, size_read, file_size, membuffer_size;
struct stat file_info;
FILE *infile;
//Find file info
if(stat(filename, &file_info)){
fprintf(stderr, "Failed to stat file: %s. %s\n", filename,
strerror(errno));
exit(1);
}
file_size = file_info.st_size;
//Open file
dprintf("Opening file: %s ...", filename);
infile = fopen(filename, "rb");
if(infile == NULL){
fprintf(stderr, "Failed to open file: %s. %s\n", filename,
strerror(errno));
exit(1);
}
dprintf("done\n");
//Read buffer
dprintf("Reading file content with size: %d ...", file_size);
membuffer_size = sizeof(u8)*file_size/sizeof(u32);
if(!(*membuffer))
free((*membuffer));
//(*membuffer) = (u32*)my_malloc(sizeof(u32)*membuffer_size);
(*membuffer) = (u32*)create_array(membuffer_size);
size_read = fread((*membuffer), sizeof(u8), file_size,infile);
fclose(infile);
if(size_read != file_size && !feof(infile)){
fprintf(stderr, "Warning: failed to read all data.\n");
}
dprintf("done.\n");
//change the endian
dprintf("Changing small-endian data to big_endian ...\n");
for(i = 0; i < membuffer_size; i++){
dprintf(" %08x->", (*membuffer)[i]);
change_endian(&(*membuffer)[i]);
dprintf("%08x", (*membuffer)[i]);
if((i+1)%4 == 0)
dprintf("\n");
}
dprintf("\ndone.\n");
dprintf("Program of size: %d loaded.\n",
(*((*membuffer)-1))*sizeof(u32));
}
void init_um32(UM32* um, u32* program){
int i;
dprintf("Initializing the Universal Machine ...");
if(program == NULL){
fprintf(stderr, "Fatal Error in init_um32, program is NULL.\n");
exit(1);
}
dprintf("\nRegisters:");
for(i=0;i<8;i++){
um->r[i] = 0;
dprintf(" %08x", um->r[i]);
}
um->finger = 0;
um->program = program;
dprintf("\nfinger = %08x\nprogram loaded at %08x\n", um->finger,
(u32)um->program);
dprintf("done.\n");
}
void dump_memory(u32 *mem){
FILE *outfile = NULL;
int i;
if(mem == NULL){
fprintf(stderr, "UM Memory is null.\n");
exit(1);
}
outfile = fopen(DUMP_FILE_NAME, "w");
dprintf("Beginning memory dump of size: %d...\n", *(mem-1));
if(!outfile){
fprintf(stderr, "Error in dump_memory. Failed to open file: %s.
%s",
DUMP_FILE_NAME, strerror(errno));
exit(1);
}
for(i=0;i<*(mem-1); i++){
fprintf(outfile, " %08x", mem[i]);
if((i+1)%8 == 0)
fprintf(outfile, "\n");
}
dprintf("Done.\n");
fclose(outfile);
}
int bound_program(UM32 um, u32 index){
if(index *(um.program-1))
return 1;
return 0;
}
int bound_array(u32 arrayID, u32 index){
if(!ARRAY(arrayID) || index GET_ARRAY_SIZE(arrayID))
return 1;
return 0;
}
/* Execute one instruction
* return 1 on success
*/
#define R(a) um->r[a]
#define PROGRAM (um->program)
#define FINGER (um->finger)
int um_run(UM32 *um, int trace){
u8 opcode;
u32 a, b, c, codex;
if(bound_program(*um, FINGER)){
printf("Array Index out of bound.\n");
exit(1);
}
codex = PROGRAM[FINGER++];
opcode = OPCODE(codex);
a = REG_A(codex);
b = REG_B(codex);
c = REG_C(codex);
if(trace)
printf("\ncodex=%08x, opcode=%d, finger=%08x\n", codex, opcode,
FINGER);
if(opcode == OP_ORTHO){
a = SPEC_A(codex);
R(a) = SPEC_V(codex);
if(trace){
printf("OP_ORTHO, r[%d]=%u\n", a, R(a));
printf("registers: %08x, %08x, %08x, %08x, %08x, %08x, %08x, %08x
\n",
R(0), R(1), R(2), R(3), R(4), R(5), R(6), R(7));
}
return 1;
}
switch(opcode){
case OP_CMOV:
if(!R(c)){
R(a) = R(b);
if(trace){
printf("OP_CMOV, r[%d]=r[%d]=%08x\n", a, b, R(a));
}
}
break;
case OP_INDEX:
if(!R(b)){
if(bound_program(*um, R(c))){
printf("Index out of bound in accessing array0 at offset %08x\n",
R(c));
exit(0);
}
R(a) = PROGRAM[R(c)];
}
else{
if(bound_array(R(b), R(c))){
printf("Index out of bound in accessing array%d\n", R(b));
exit(1);
}
R(a) = ARRAY(R(b))[R(c)];
}
if(trace)
printf("OP_INDEX, r[%d]=array[%d][%d]=%08x\n", a, b, c, R(a));
break;
case OP_AMAND:
if(!R(a)){
if(bound_program(*um, R(b))){
printf("Index out of bound in accessing array0 at offset %08x\n",
R(c));
exit(0);
}
PROGRAM[R(b)] = R(c);
}
else{
if(bound_array(R(a), R(b))){
printf("Index out of bound in accessing array%d\n", R(b));
exit(1);
}
ARRAY(R(a))[R(b)] = R(c);
}
if(trace)
printf("OP_AMAND, array[%d][%d]=r[%d]=%08x\n", a, b, c, R(c));
break;
case OP_ADD:
R(a) = R(b) + R(c);
if(trace)
printf("OP_ADD, r[%d]=r[%d]+r[%d]=%08x\n", a, b, c, R(a));
break;
case OP_MUL:
R(a) = R(b) * R(c);
if(trace)
printf("OP_ADD, r[%d]=r[%d]*r[%d]=%08x\n", a, b, c, R(a));
break;
case OP_DIV:
R(a) = R(b) / R(c);
if(trace)
printf("OP_ADD, r[%d]=r[%d]/r[%d]=%08x\n", a, b, c, R(a));
break;
case OP_NAND:
R(a) = ~(R(b) & R(c));
if(trace)
printf("OP_ADD, r[%d]=~(r[%d]&r[%d])=%08x\n", a, b, c, R(a));
break;
case OP_HALT:
if(trace)
printf("OP_HALT, stopping program ... done.\n");
return 0;
break;
case OP_ALLOC:
R(b) = create_array(R(c));
if(trace)
printf("OP_ALLOC, r[%d]=create_array(r[%d])=%08x\n", b, c, R(b));
break;
case OP_FREE:
delete_array(R(c));
R(c) = 0;
if(trace)
printf("OP_REE, free(r[%d]=%08x)\n", c, R(c));
break;
case OP_OUT:
if(R(c) < 256){
putchar(R(c));
if(trace)
printf("OP_OUT, putchar(r[%d]=%08x)\n", c, R(c));
}
else
fprintf(stdout, "Warning, failed to output value %08x as char.\n",
R(c));
break;
case OP_IN:
R(c)=getchar();
if(trace)
printf("OP_IN, r[%d]=getchar()=%08x", c, R(c));
break;
case OP_LOAD:
if(R(b) != 0)
copy2array0(um, R(b));
FINGER = R(c);
if(trace){
printf("OP_LOAD, loading program into array0 from array[%d] at %08x
\n",
b, R(b));
printf("\tfinger=%08x\n", FINGER);
}
break;
default:
printf("Fatal Error: Bad opcode=%d, codex=%08x\n", opcode, codex);
return 0;
break;
}
if(trace)
printf("registers: %08x, %08x, %08x, %08x, %08x, %08x, %08x, %08x
\n",
R(0), R(1), R(2), R(3), R(4), R(5), R(6), R(7));
return 1;
}
int main(int argc, char *argv[]){
UM32 um;
u32 *program;
int run;
#ifndef NOT_GDB
argc = 2;
argv[1] = "sandmark.umz";
#endif
if(argc < 2){
printf("Usage: %s filename\n", argv[0]);
exit(0);
}
//load program
load_program(&program, argv[1]);
//initialize um
init_um32(&um, program);
#ifdef DUMP_MEMORY
dump_memory(program);
#endif
do{
run = um_run(&um, 1);
}while(run);
return 0;
}