#include #include #include #include #include typedef enum {opDAT, opMOV, opADD, opSUB, opJMP, opJMZ, opJMG, opDJN, opCMP} OpCode; const OpCode lastop = opCMP; typedef enum {immediate, direct, indirect, decrement} AddressingMode; const AddressingMode lastam = decrement; enum { memsize = 8000 }; typedef short Value; // [0..memsize-1] typedef struct { AddressingMode addrmode; Value val; } Operand; typedef struct { OpCode opcode; Operand a, b; } Instruction; typedef struct { char* name; Value pc; } Process; typedef Instruction Memory[memsize]; /* printing procedures */ void print_operand(Operand* op) { switch (op->addrmode) { case direct: break; case immediate: putchar('#'); break; case indirect: putchar('@'); break; case decrement: putchar('<'); break; } printf("%d", op->val); } void print_instruction(Instruction* ip) { switch (ip->opcode) { case opDAT: printf("DAT"); break; case opMOV: printf("MOV"); break; case opADD: printf("ADD"); break; case opSUB: printf("SUB"); break; case opJMP: printf("JMP"); break; case opJMZ: printf("JMZ"); break; case opJMG: printf("JMG"); break; case opDJN: printf("DJN"); break; case opCMP: printf("CMP"); break; } putchar('\t'); switch (ip->opcode) { case opDAT: print_operand(&ip->b); break; case opJMP: print_operand(&ip->a); break; default: print_operand(&ip->a); putchar(','); print_operand(&ip->b); break; } } Value adjust_value(Value val) { Value remainder = val % memsize; if (val >= 0 || remainder == 0) { return remainder; } else { return remainder + memsize; } } bool load_val(FILE* fp, Value* val) { if (fscanf(fp, "%hd", val) != 1) return false; *val = adjust_value(*val); return true; } /* read an instruction from fp and store it into op; return true in case of success */ bool load_operand(FILE* fp, Operand* op) { if (fscanf(fp, "%u", &op->addrmode) != 1) return false; if (op->addrmode < 0 || op->addrmode > lastam) return false; if (!load_val(fp, &op->val)) return false; return true; } /* read an instruction from the filepointer and store it into ip; return true in case of success */ bool load_instruction(FILE* fp, Instruction* ip) { if (fscanf(fp, "%u", &ip->opcode) != 1) return false; if (ip->opcode < 0 || ip->opcode > lastop) return false; switch (ip->opcode) { case opDAT: ip->b.addrmode = immediate; return load_val(fp, &ip->b.val); case opJMP: return load_operand(fp, &ip->a); default: return load_operand(fp, &ip->a) && load_operand(fp, &ip->b); } } /* load object code from the given filename, load it into mem[], beginning with mem[0], up to maxsize instructions are stored, the length of the stored program is returned; return -1 in case of failures */ Value load(char* filename, Instruction mem[], Value maxsize) { FILE* fp = fopen(filename, "r"); if (!fp) return -1; Instruction* ip = mem; while (ip < mem + maxsize && load_instruction(fp, ip)) { ++ip; } fclose(fp); return ip - mem; } /* copy progsize instructions from p to mem at addr; returns initial program counter */ Value load_program(Memory mem, Instruction p[], Value addr, Value progsize) { bool found = false; // instruction already seen? Value pc = addr; // initial pc for (int i = 0; i < progsize; ++i) { if (!found && p[i].opcode != opDAT) { pc = (addr + i) % memsize; found = true; } mem[(addr + i) % memsize] = p[i]; } return pc; } /* return absolute address from an operand */ Value get_operand_address(Value pc, Operand* op, Memory mem) { if (op->addrmode == immediate) return 0; /* should not happen */ Value val = (pc + op->val) % memsize; if (op->addrmode == direct) return val; Value* vp = &mem[val].b.val; if (op->addrmode == decrement) { *vp = (*vp + memsize - 1) % memsize; } return (val + *vp) % memsize; } /* return operand as instruction */ Instruction get_operand(Value pc, Operand* op, Memory mem) { if (op->addrmode == immediate) { return (Instruction) {opDAT, {immediate, 0}, {immediate, op->val}}; } Value addr = get_operand_address(pc, op, mem); return mem[addr]; } /* return numerical value of an operand */ Value get_value(Value pc, Operand* op, Memory mem) { return get_operand(pc, op, mem).b.val; } /* execute one step of p; return false in case of a crash */ bool execute(Process* p, Memory mem) { Value pc = p->pc; p->pc = (p->pc + 1) % memsize; Instruction* ip = &mem[pc]; printf("%-12s [%04d] ", p->name, pc); print_instruction(ip); putchar('\n'); Instruction src; Value dest; switch (ip->opcode) { case opDAT: return false; /* crash */ case opMOV: src = get_operand(pc, &ip->a, mem); dest = get_operand_address(pc, &ip->b, mem); mem[dest] = src; break; case opADD: src = get_operand(pc, &ip->a, mem); dest = get_operand_address(pc, &ip->b, mem); mem[dest].b.val = adjust_value(mem[dest].b.val + src.b.val); break; case opSUB: src = get_operand(pc, &ip->a, mem); dest = get_operand_address(pc, &ip->b, mem); mem[dest].b.val = adjust_value(mem[dest].b.val - src.b.val); break; case opJMP: p->pc = get_operand_address(pc, &ip->a, mem); break; case opJMZ: if (get_value(pc, &ip->b, mem) == 0) { p->pc = get_operand_address(pc, &ip->a, mem); } break; case opJMG: if (get_value(pc, &ip->b, mem) > 0) { p->pc = get_operand_address(pc, &ip->a, mem); } break; case opDJN: dest = get_operand_address(pc, &ip->b, mem); if (--mem[dest].b.val) { p->pc = get_operand_address(pc, &ip->a, mem); } case opCMP: { Value val1 = get_value(pc, &ip->a, mem); Value val2 = get_value(pc, &ip->b, mem); if (val1 == val2) { p->pc = (pc + 2) % memsize; } break; } } return true; } int main(int argc, char** argv) { /* load the two programs */ if (argc != 3) return 1; const Value maxprogsize = 256; Instruction prog[2][maxprogsize]; Value size1 = load(argv[1], prog[0], maxprogsize); if (size1 < 0) return 1; Value size2 = load(argv[2], prog[1], maxprogsize); if (size2 < 0) return 1; /* load both programs at random locations into memory */ Memory mem = {{0}}; srand(getpid() ^ time(0)); Value loc1 = rand() % memsize; Value pc1 = load_program(mem, prog[0], loc1, size1); const Value mindist = 300; const Value remaining_space = memsize - size1 - size2 - 2 * mindist; Value loc2 = (loc1 + size1 + mindist + rand() % remaining_space) % memsize; Value pc2 = load_program(mem, prog[1], loc2, size2); /* start execution */ Process p[] = {{argv[1], pc1}, {argv[2], pc2}}; for(;;) { /* run until a process crashes */ for (int pi = 0; pi < 2; ++pi) { if (!execute(&p[pi], mem)) { printf("%s crashed.\n", p[pi].name); return 0; } } } }