/** * Machine emulator for the ULNAv2-C instruction set. * * Reads an ulnav2-c image file and executes it within a virtual machine. * * Copyright(c) 2020-2021,2024 Jason Tang * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #include static uint16_t *dmem; static uint16_t *imem; static uint16_t pc; static uint16_t reg_file[8]; static struct { bool carry; bool negative; bool overflow; bool zero; } cond_codes; static unsigned cpu_cycle = 0; static bool exec_vm = true; static int ticks_to_run = 0; static bool run_to_finish = false; static bool show_rtn = false; static bool show_vm_status = false; #ifdef FEATURE_MUL static uint32_t mul_product; #endif static void reset_cpu() { dmem = (uint16_t *) calloc((1 << 16), sizeof(*dmem)); imem = (uint16_t *) calloc((1 << 16), sizeof(*imem)); if (!dmem || !imem) { fprintf(stderr, "Could not allocate memory\n"); exit(EXIT_FAILURE); } } static void load_image(const char *image_filename) { FILE *f = fopen(image_filename, "r"); if (!f) { fprintf(stderr, "Could not open %s\n", image_filename); exit(EXIT_FAILURE); } char line[80]; bool first_line = true; int lineno = 0; uint16_t *next_imem = imem; while (fgets(line, sizeof(line), f) != NULL) { lineno++; if (first_line) { first_line = false; continue; } if (strlen(line) < 4) { fprintf(stderr, "Invalid line %d\n", lineno); exit(EXIT_FAILURE); } line[4] = '\0'; char *endptr; unsigned long val = strtoul(line, &endptr, 16); if (*endptr != '\0') { fprintf(stderr, "Invalid line %d\n", lineno); exit(EXIT_FAILURE); } *next_imem = val; next_imem++; } } static std::string format(const std::string fmt, ...) { va_list ap; va_start(ap, fmt); size_t len = vsnprintf(nullptr, 0, fmt.c_str(), ap); va_end(ap); va_start(ap, fmt); std::vector v(len+1); vsnprintf(&v[0], len + 1, fmt.c_str(), ap); va_end(ap); return &v[0]; } static void print_vm_status() { printf("-- Cycle = %u, PC = 0x%04x --\n", cpu_cycle, pc); printf("Register File:\n"); for (unsigned i = 0; i < 8; i++) { printf(" %d: 0x%04x", i, reg_file[i]); if (i % 4 == 3) { printf("\n"); } } printf("Condition Codes:\n"); printf(" C: %c N: %c V: %c Z: %c\n", (cond_codes.carry ? 't' : 'f'), (cond_codes.negative ? 't' : 'f'), (cond_codes.overflow ? 't' : 'f'), (cond_codes.zero ? 't' : 'f')); printf("Data Memory:\n"); uint16_t last_val = 0; bool print_line = true; bool print_elipses = true; for (unsigned i = 0; i < 65536; i += 8) { if (i == (65536 - 8)) { print_line = true; } if (!print_line) { for (unsigned j = 0; j < 8; j++) { if (dmem[i + j] != last_val) { print_line = true; } } } if (!print_line) { if (print_elipses) { printf(" ...\n"); print_elipses = false; } continue; } printf(" 0x%04x: ", i); last_val = dmem[i]; print_line = false; for (unsigned j = 0; j < 8; j++) { printf(" %04x", dmem[i + j]); if (dmem[i + j] != last_val) { print_line = true; } } printf("\n"); print_elipses = true; } } static int16_t sign_extend(uint16_t imm, unsigned num_orig_bits) { int16_t val = 0; if (imm & (1 << (num_orig_bits - 1))) { val = ~0; } val = (val & ~((1 << num_orig_bits) - 1)) | (imm & ((1 << num_orig_bits) - 1)); return val; } static void update_cond_codes(uint16_t aval, uint16_t bval) { uint32_t sum = aval + bval; cond_codes.carry = cond_codes.negative = cond_codes.overflow = cond_codes.zero = false; if (sum >= (1 << 16)) { cond_codes.carry = true; } if (sum & (1 << 15)) { cond_codes.negative = true; } if (((aval & (1 << 15)) == (bval & (1 << 15))) && ((aval & (1 << 15)) != (sum & (1 << 15)))) { cond_codes.overflow = true; } if ((sum & 0xffff) == 0) { cond_codes.zero = true; } } static uint16_t dmem_read(uint16_t addr) { return dmem[addr]; } static void dmem_write(uint16_t addr, uint16_t val) { dmem[addr] = val; } /** Abstract parent class to all ULNAv2 instructions */ class Instr { public: Instr() = delete; Instr(uint16_t instr) : _instr(instr) {} virtual ~Instr() = default; const std::string &get_instr() const { return _instr_str; } const std::string &get_rtn() const { return _rtn_str; } virtual void exec() const = 0; protected: uint16_t _instr; std::string _instr_str; std::string _rtn_str; }; class Instr_ClassA : public Instr { public: Instr_ClassA(uint16_t instr) : Instr(instr) { _ra = (instr >> 5) & 0x7; _rb = (instr >> 2) & 0x7; _rw = (instr >> 8) & 0x7; } protected: unsigned _ra; unsigned _rb; unsigned _rw; }; class Instr_ClassB : public Instr { public: Instr_ClassB(uint16_t instr) : Instr(instr) { _ra = (instr >> 5) & 0x7; _rw = (instr >> 8) & 0x7; _imm5 = instr & 0x1f; _simm16 = sign_extend(_imm5, 5); } protected: unsigned _ra; unsigned _rw; uint8_t _imm5; int16_t _simm16; }; class Instr_ClassB_Signed : public Instr_ClassB { public: Instr_ClassB_Signed(uint16_t instr) : Instr_ClassB(instr) { _sign = (_imm5 >> 4) & 1; _magnitude = _imm5 & 0x0f; _simm16 = _magnitude; if (_sign) { _simm16 = -1 * _simm16; } } protected: unsigned _sign; unsigned _magnitude; }; class Instr_ClassC : public Instr { public: Instr_ClassC(uint16_t instr) : Instr(instr) { _rw = (instr >> 8) & 0x7; _imm8 = instr & 0xff; } protected: unsigned _rw; uint8_t _imm8; }; class Instr_ClassD : public Instr { public: Instr_ClassD(uint16_t instr) : Instr(instr) { _imm11 = instr & 0x7ff; _simm16 = sign_extend(_imm11, 11); _imm16 = static_cast(_simm16); } protected: uint16_t _imm11; int16_t _simm16; uint16_t _imm16; }; /* instruction 0: or. */ class Instr_or : public Instr_ClassA { public: Instr_or(uint16_t instr) : Instr_ClassA(instr) { _instr_str = format("OR. r%u, r%u, r%u", _rw, _ra, _rb); _rtn_str = format("R[%u] <- R[%u] OR R[%u]", _rw, _ra, _rb); } virtual void exec() const { reg_file[_rw] = reg_file[_ra] | reg_file[_rb]; update_cond_codes(reg_file[_rw], 0); pc++; } }; /* instruction 1: add. */ class Instr_add : public Instr_ClassA { public: Instr_add(uint16_t instr) : Instr_ClassA(instr) { _instr_str = format("ADD. r%u, r%u, r%u", _rw, _ra, _rb); _rtn_str = format("R[%u] <- R[%u] + R[%u]", _rw, _ra, _rb); } virtual void exec() const { update_cond_codes(reg_file[_ra], reg_file[_rb]); reg_file[_rw] = reg_file[_ra] + reg_file[_rb]; pc++; } }; /* instruction 2: and. */ class Instr_and : public Instr_ClassA { public: Instr_and(uint16_t instr) : Instr_ClassA(instr) { _instr_str = format("AND. r%u, r%u, r%u", _rw, _ra, _rb); _rtn_str = format("R[%u] <- R[%u] AND R[%u]", _rw, _ra, _rb); } virtual void exec() const { reg_file[_rw] = reg_file[_ra] & reg_file[_rb]; update_cond_codes(reg_file[_rw], 0); pc++; } }; /* instruction 3: stw */ class Instr_stw : public Instr_ClassA { public: Instr_stw(uint16_t instr) : Instr_ClassA(instr) { _instr_str = format("STW r%u, r%u, r%u", _rw, _ra, _rb); _rtn_str = format("Mem[R[%u] + R[%u] <- R[%u]", _ra, _rb, _rw); } virtual void exec() const { dmem_write(reg_file[_ra] + reg_file[_rb], reg_file[_rw]); pc++; } }; /* instruction 4: br */ class Instr_br : public Instr_ClassA { public: Instr_br(uint16_t instr) : Instr_ClassA(instr) { _instr_str = format("BR r%u", _ra); _rtn_str = format("PC <- R[%u]", _ra); } virtual void exec() const { pc = reg_file[_ra]; } }; /* instruction 5: cmp. */ class Instr_cmp : public Instr_ClassA { public: Instr_cmp(uint16_t instr) : Instr_ClassA(instr) { _instr_str = format("CMP. r%u, r%u", _ra, _rb); _rtn_str = format("update cond codes based on R[%u] - R[%u]", _ra, _rb); } virtual void exec() const { uint16_t bval = ~reg_file[_rb] + 1; update_cond_codes(reg_file[_ra], bval); pc++; } }; #ifdef FEATURE_MUL /* instruction 6: mul */ class Instr_mul : public Instr_ClassA { public: Instr_mul(uint16_t instr) : Instr_ClassA(instr) { _instr_str = format("MUL r%u, r%u, r%u", _rw, _ra, _rb); _rtn_str = format("Product[31:0] <- R[%u] UMUL R[%u]; R[%u] <- Product[15:0]", _ra, _rb, _rw); } virtual void exec() const { mul_product = reg_file[_ra] * reg_file[_rb]; reg_file[_rw] = mul_product & 0xffff; cpu_cycle += 17; pc++; } }; #endif /* instruction 7: ldw */ class Instr_ldw : public Instr_ClassA { public: Instr_ldw(uint16_t instr) : Instr_ClassA(instr) { _instr_str = format("LDW r%u, r%u, r%u", _rw, _ra, _rb); _rtn_str = format("R[%u] <- Mem[R[%u] + R[%u]]", _rw, _ra, _rb); } virtual void exec() const { reg_file[_rw] = dmem_read(reg_file[_ra] + reg_file[_rb]); pc++; } }; /* instruction 8: ash */ class Instr_ash : public Instr_ClassB_Signed { public: Instr_ash(uint16_t instr) : Instr_ClassB_Signed(instr) { _instr_str = format("ASH r%u, r%u, %d", _rw, _ra, _simm16); _rtn_str = format("R[%u] <- R[%u] ashift %d", _rw, _ra, _simm16); } virtual void exec() const { int16_t v = *((int16_t *)®_file[_ra]); if (_sign == 0) { reg_file[_rw] = v >> _magnitude; } else { reg_file[_rw] = v << _magnitude; } pc++; } }; /* instruction 9: addi. */ class Instr_addi : public Instr_ClassB { public: Instr_addi(uint16_t instr) : Instr_ClassB(instr) { _instr_str = format("ADDI. r%u, r%u, %d", _rw, _ra, _simm16); _rtn_str = format("R[%u] <- R[%u] + %d", _rw, _ra, _simm16); } virtual void exec() const { update_cond_codes(reg_file[_ra], *((uint16_t *)&_simm16)); reg_file[_rw] = reg_file[_ra] + _simm16; pc++; } }; /* instruction 10: lsh */ class Instr_lsh : public Instr_ClassB_Signed { public: Instr_lsh(uint16_t instr) : Instr_ClassB_Signed(instr) { _instr_str = format("LSH r%u, r%u, %d", _rw, _ra, _simm16); _rtn_str = format("R[%u] <- R[%u] lshift %d", _rw, _ra, _simm16); } virtual void exec() const { if (_sign == 0) { reg_file[_rw] = reg_file[_ra] >> _magnitude; } else { reg_file[_rw] = reg_file[_ra] << _magnitude; } pc++; } }; /* instruction 11: stwi */ class Instr_stwi : public Instr_ClassB { public: Instr_stwi(uint16_t instr) : Instr_ClassB(instr) { _instr_str = format("STWI r%u, r%u, %d", _rw, _ra, _simm16); _rtn_str = format("Mem[R[%u] + %d] <- R[%u]", _ra, _simm16, _rw); } virtual void exec() const { dmem_write(reg_file[_ra] + _simm16, reg_file[_rw]); pc++; } }; /* instruction 12: negi. */ class Instr_negi : public Instr_ClassB { public: Instr_negi(uint16_t instr) : Instr_ClassB(instr) { _instr_str = format("NEGI. r%u, r%u, %d", _rw, _ra, _simm16); _rtn_str = format("R[%u] <- NOT R[%u] + %d", _rw, _ra, _simm16); } virtual void exec() const { update_cond_codes(~reg_file[_ra], *((uint16_t *)&_simm16)); reg_file[_rw] = ~reg_file[_ra] + _simm16; pc++; } }; /* instruction 13: brl */ class Instr_brl : public Instr_ClassB { public: Instr_brl(uint16_t instr) : Instr_ClassB(instr) { _instr_str = format("BRL r%u, r%u", _rw, _ra); _rtn_str = format("R[%u] <- PC + 1; PC <- R[%u]", _rw, _ra); } virtual void exec() const { reg_file[_rw] = pc + 1; pc = reg_file[_ra]; } }; /* instruction 14: rot */ class Instr_rot : public Instr_ClassB_Signed { public: Instr_rot(uint16_t instr) : Instr_ClassB_Signed(instr) { _instr_str = format("ROT r%u, r%u, %d", _rw, _ra, _simm16); _rtn_str = format("R[%u] <- R[%u] rotate %d", _rw, _ra, _simm16); } virtual void exec() const { uint16_t old_aval = reg_file[_ra]; uint16_t new_wval; if (_sign == 0) { uint16_t mask = (1 << _magnitude) - 1; new_wval = old_aval >> _magnitude; new_wval |= (old_aval & mask) << (16 - _magnitude); } else { uint16_t mask = (1 << (16 - _magnitude)) - 1; new_wval = old_aval << _magnitude; new_wval |= (old_aval >> (16 - _magnitude)) & mask; } reg_file[_rw] = new_wval; pc++; } }; /* instruction 15: ldwi */ class Instr_ldwi : public Instr_ClassB { public: Instr_ldwi(uint16_t instr) : Instr_ClassB(instr) { _instr_str = format("LDWI r%u, r%u, %d", _rw, _ra, _simm16); _rtn_str = format("R[%u] <- Mem[R[%u] + %d]", _rw, _ra, _simm16); } virtual void exec() const { reg_file[_rw] = dmem_read(reg_file[_ra] + _simm16); pc++; } }; /* instruction 16: ori. */ class Instr_ori : public Instr_ClassC { public: Instr_ori(uint16_t instr) : Instr_ClassC(instr) { _instr_str = format("ORI. r%u, 0x%02x", _rw, _imm8); _rtn_str = format("R[%u] <- R[%u] OR 0x%04x", _rw, _rw, _imm8); } virtual void exec() const { reg_file[_rw] = reg_file[_rw] | (uint16_t) _imm8; update_cond_codes(reg_file[_rw], 0); pc++; } }; #ifdef FEATURE_MUL /* instruction 17: movup */ class Instr_movup : public Instr_ClassC { public: Instr_movup(uint16_t instr) : Instr_ClassC(instr) { _instr_str = format("MOVUP r%u", _rw); _rtn_str = format("R[%u] <- Product[31:16] logic.rshift 16", _rw); } virtual void exec() const { reg_file[_rw] = (mul_product >> 16) & 0xffff; pc++; } }; #endif /* instruction 18: andi. */ class Instr_andi : public Instr_ClassC { public: Instr_andi(uint16_t instr) : Instr_ClassC(instr) { _imm16 = 0xff00 | _imm8; _instr_str = format("ANDI. r%u, 0x%02x", _rw, _imm8); _rtn_str = format("R[%u] <- R[%u] AND 0x%04x", _rw, _rw, _imm16); } virtual void exec() const { reg_file[_rw] = reg_file[_rw] & _imm16; update_cond_codes(reg_file[_rw], 0); pc++; } private: uint16_t _imm16; }; /* instruction 20: movi */ class Instr_movi : public Instr_ClassC { public: Instr_movi(uint16_t instr) : Instr_ClassC(instr) { _instr_str = format("MOVI r%u, 0x%02x", _rw, _imm8); _rtn_str = format("R[%u] <- 0x%04x", _rw, _imm8); } virtual void exec() const { reg_file[_rw] = _imm8; pc++; } }; /* instruction 21: cmpi. */ class Instr_cmpi : public Instr_ClassC { public: Instr_cmpi(uint16_t instr) : Instr_ClassC(instr) { _simm16 = sign_extend(_imm8, 8); _instr_str = format("CMPI. r%u, %d", _rw, _simm16); _rtn_str = format("update cond codes based on R[%u] - %d", _rw, _simm16); } virtual void exec() const { uint16_t imm16 = *((uint16_t *)&_simm16); imm16 = (~imm16) + 1; update_cond_codes(reg_file[_rw], imm16); pc++; } private: int16_t _simm16; }; /* instruction 22: movis */ class Instr_movis : public Instr_ClassC { public: Instr_movis(uint16_t instr) : Instr_ClassC(instr) { _imm16 = _imm8 << 8; _instr_str = format("MOVIS r%u, 0x%02x", _rw, _imm8); _rtn_str = format("R[%u] <- R[%u][7:0] OR 0x%04x", _rw, _rw, _imm16); } virtual void exec() const { reg_file[_rw] = (reg_file[_rw] & 0xff) | _imm16; pc++; } private: uint16_t _imm16; }; /* instruction 23: bl */ class Instr_bl : public Instr_ClassC { public: Instr_bl(uint16_t instr) : Instr_ClassC(instr) { _simm16 = sign_extend(_imm8, 8); _instr_str = format("BL r%u, %d", _rw, _simm16); _rtn_str = format("R[%u] <- PC + 1; PC <- PC + %d", _rw, _simm16); } virtual void exec() const { reg_file[_rw] = pc + 1; pc = pc + _simm16; } private: int16_t _simm16; }; /* instruction 24: b */ class Instr_b : public Instr_ClassD { public: Instr_b(uint16_t instr) : Instr_ClassD(instr) { _instr_str = format("B %d", _simm16); _rtn_str = format("PC <- PC + %d", _simm16); } virtual void exec() const { pc = pc + _simm16; } }; /* instruction 25: b.gt */ class Instr_bgt : public Instr_ClassD { public: Instr_bgt(uint16_t instr) : Instr_ClassD(instr) { _instr_str = format("B.GT %d", _simm16); _rtn_str = format("PC <- PC + %d if Z == 0 AND N == V", _simm16); } virtual void exec() const { if (!cond_codes.zero && (cond_codes.negative == cond_codes.overflow)) { pc = pc + _simm16; } else { pc++; } } }; /* instruction 26: b.eq */ class Instr_beq : public Instr_ClassD { public: Instr_beq(uint16_t instr) : Instr_ClassD(instr) { _instr_str = format("B.EQ %d", _simm16); _rtn_str = format("PC <- PC + %d if Z == 1", _simm16); } virtual void exec() const { if (cond_codes.zero) { pc = pc + _simm16; } else { pc++; } } }; /* instruction 27: b.ge */ class Instr_bge : public Instr_ClassD { public: Instr_bge(uint16_t instr) : Instr_ClassD(instr) { _instr_str = format("B.GE %d", _simm16); _rtn_str = format("PC <- PC + %d if N == V", _simm16); } virtual void exec() const { if (cond_codes.negative == cond_codes.overflow) { pc = pc + _simm16; } else { pc++; } } }; /* instruction 28: b.lt */ class Instr_blt : public Instr_ClassD { public: Instr_blt(uint16_t instr) : Instr_ClassD(instr) { _instr_str = format("B.LT %d", _simm16); _rtn_str = format("PC <- PC + %d if NOT (N == V)", _simm16); } virtual void exec() const { if (cond_codes.negative != cond_codes.overflow) { pc = pc + _simm16; } else { pc++; } } }; /* instruction 29: b.ne */ class Instr_bne : public Instr_ClassD { public: Instr_bne(uint16_t instr) : Instr_ClassD(instr) { _instr_str = format("B.NE %d", _simm16); _rtn_str = format("PC <- PC + %d if Z == 0", _simm16); } virtual void exec() const { if (!cond_codes.zero) { pc = pc + _simm16; } else { pc++; } } }; /* instruction 30: b.le */ class Instr_ble : public Instr_ClassD { public: Instr_ble(uint16_t instr) : Instr_ClassD(instr) { _instr_str = format("B.LE %d", _simm16); _rtn_str = format("PC <- PC + %d if NOT (Z == 0 AND N == V)", _simm16); } virtual void exec() const { if (!((!cond_codes.zero) && (cond_codes.negative == cond_codes.overflow))) { pc = pc + _simm16; } else { pc++; } } }; /* instruction 31: halt */ class Instr_halt : public Instr_ClassD { public: Instr_halt(uint16_t instr) : Instr_ClassD(instr) { _instr_str = format("HALT 0x%04x", _imm16); _rtn_str = format("Mem[0xffff] <- 0x%04x; PC <- PC", _imm16); } virtual void exec() const { dmem_write(0xffff, _imm16); exec_vm = false; } }; class Instr_undefined : public Instr { public: Instr_undefined(uint16_t instr) : Instr(instr) { _instr_str = format("UNDEF 0x%04x", _instr); _rtn_str = format("Undefined instruction 0x%04x", _instr); } virtual void exec() const { exec_vm = false; } }; static Instr *decode_instr(uint16_t instr) { unsigned instr_num = (instr >> 11) & (0x1f); switch (instr_num) { case 0: return new Instr_or(instr); case 1: return new Instr_add(instr); case 2: return new Instr_and(instr); case 3: return new Instr_stw(instr); case 4: return new Instr_br(instr); case 5: return new Instr_cmp(instr); #ifdef FEATURE_MUL case 6: return new Instr_mul(instr); #endif case 7: return new Instr_ldw(instr); case 8: return new Instr_ash(instr); case 9: return new Instr_addi(instr); case 10: return new Instr_lsh(instr); case 11: return new Instr_stwi(instr); case 12: return new Instr_negi(instr); case 13: return new Instr_brl(instr); case 14: return new Instr_rot(instr); case 15: return new Instr_ldwi(instr); case 16: return new Instr_ori(instr); #ifdef FEATURE_MUL case 17: return new Instr_movup(instr); #endif case 18: return new Instr_andi(instr); case 20: return new Instr_movi(instr); case 21: return new Instr_cmpi(instr); case 22: return new Instr_movis(instr); case 23: return new Instr_bl(instr); case 24: return new Instr_b(instr); case 25: return new Instr_bgt(instr); case 26: return new Instr_beq(instr); case 27: return new Instr_bge(instr); case 28: return new Instr_blt(instr); case 29: return new Instr_bne(instr); case 30: return new Instr_ble(instr); case 31: return new Instr_halt(instr); } return new Instr_undefined(instr); }; static void print_help() { printf("? display this list\n"); printf("s step into and execute next instruction\n"); printf(" execute the next instructions\n"); printf("c continue executing instructions\n"); printf("status show current status of the virtual machine\n"); printf("showrtn toggle showing RTN prior to executing instruction\n"); printf("showstatus toggle showing VM status after executing instruction\n"); printf("quit quit this program\n"); /* TODO: implement these commands: next | if next = bl then continue until br, otherwise step b | break - set breakpoint at PC d | delete - remove breakpoint at PC t | tbreak - temporary breakpoint at PC i | info - list all breakpoints */ } static void prompt_user(Instr *next_instr) { std::string prompt = format("PC 0x%04x: %s > ", pc, next_instr->get_instr().c_str()); char * buf = readline(prompt.c_str()); if (buf == NULL || strcmp(buf, "quit") == 0) { exec_vm = false; return; } if (*buf == '\0') { return; } else { add_history(buf); } if (strcmp(buf, "?") == 0 || strcmp(buf, "help") == 0) { print_help(); return; } if (strcmp(buf, "s") == 0 || strcmp(buf, "step") == 0) { ticks_to_run = 1; return; } char *endptr; long val = strtol(buf, &endptr, 10); if (*endptr == '\0' && val > 0) { ticks_to_run = val; return; } if (strcmp(buf, "c") == 0 || strcmp(buf, "cont") == 0 || strcmp(buf, "continue") == 0) { ticks_to_run = -1; return; } if (strcmp(buf, "f") == 0 || strcmp(buf, "finish") == 0) { run_to_finish = true; ticks_to_run = -1; return; } if (strcmp(buf, "status") == 0) { print_vm_status(); return; } if (strcmp(buf, "showrtn") == 0) { show_rtn = !show_rtn; return; } if (strcmp(buf, "showstatus") == 0) { show_vm_status = !show_vm_status; return; } printf("Unknown command. Enter '?' to display list of commands.\n"); } static void execute_image() { while (exec_vm) { uint16_t instr = imem[pc]; Instr *next_instr = decode_instr(instr); if (ticks_to_run == 0) { prompt_user(next_instr); delete next_instr; continue; } if (show_rtn) { printf("%s\n", next_instr->get_rtn().c_str()); } next_instr->exec(); cpu_cycle++; if (show_vm_status) { print_vm_status(); } Instr_br *t; if (run_to_finish && ((t = dynamic_cast(next_instr))) != NULL) { run_to_finish = 0; ticks_to_run = 0; } delete next_instr; if (ticks_to_run > 0) { ticks_to_run--; } } } static void print_usage(const char *argv0) { printf("Usage: %s [OPTIONS] FILE\n", argv0); printf("Where:\n"); printf(" FILE Logisim-formatted image file to execute\n"); printf("Options:\n"); printf(" -r show RTN before each instruction execution\n"); printf(" -v show VM status after each instruction execution\n"); } int main(int argc, char *argv[]) { reset_cpu(); int ch; while ((ch = getopt(argc, argv, "rv")) != -1) { switch (ch) { case 'r': show_rtn = true; break; case 'v': show_vm_status = true; break; case '?': default: print_usage(argv[0]); exit(EXIT_SUCCESS); } } if (optind + 1 > argc) { fprintf(stderr, "Need an image file\n"); print_usage(argv[0]); exit(EXIT_FAILURE); } rl_bind_key('\t', rl_insert); load_image(argv[optind]); printf("ULNAv2-C emulator. Enter '?' to display list of commands.\n\n"); execute_image(); printf("VM halted!\n"); print_vm_status(); return 0; }