// file: isc.cc // author: Robert M. Keller // purpose: ISC (Incredibly-Simple Computer) simulator, loader, assembler // $Id: isc.cc,v 1.9 1997/04/05 21:38:55 keller Exp keller $ // Modified 3/25/99 at the request of M. Fleck to permit number of registers // to be specified on the command line. Any integer number of registers can // now be used. // // Command line syntax is now // isc [-][l][s][t][r NNNN] // where NNNN is the number of registers. There should be no space between // the command-line letter options, e.g. // isc -tr 1000 rfac.isc // means used program rfac.isc, with trace option and 1000 registers int REGISTERS = 32; // default number of machine registers const int MEMORY = 4096; // memory words const int INPUT_WORD = -1; // word from input device const int INPUT_STATUS = -2; // status of input device const int OUTPUT_WORD = -3; // word to output device const int OUTPUT_STATUS = -4; // status of output device const int MAX_IMMEDIATE = 524288; // maximum value of immediate const int MIN_IMMEDIATE = -524287; // minimum value of immediate const int CBUFFSIZE = 100; // source input buffer #include #include #include #include #include #include "safe_array.H" #include "symbol_table.h" typedef int address; // type of a memory address typedef unsigned short reg_index; // type of a register index typedef int data; // type of data word enum opcode // enumeration of opcodes {LOAD, STORE, COPY, ADD, SUB, MUL, DIV, SHR, SHL, AND, OR, COMP, JEQ, JNE, JGT, JGTE, JLT, JLTE, JSUB, JUNC, LIM, AIM}; opcode opcodes[] = {LOAD, STORE, COPY, ADD, SUB, MUL, DIV, SHR, SHL, AND, OR, COMP, JEQ, JNE, JGT, JGTE, JLT, JLTE, JSUB, JUNC, LIM, AIM}; enum psuedo_opcode // enumeration of pseudo-opcodes {ORIGIN, DEFINE, REGISTER, USE, RELEASE, TRACE, LABEL}; enum input_statuses { END_OF_FILE = -1, INPUT_BUSY = 0, INPUT_READY = 1 }; enum output_statuses { OUTPUT_BUSY = 0, OUTPUT_READY = 1 }; typedef int int24; // supposed to represent 24-bit signed integer symbol_table ops(100); symbol_table pops(100); symbol_table id(100); // symbol table for user identifiers symbol_table regid(100); // symbol table for register identifiers struct instruction // The structure of an instruction: { opcode op; reg_index rix[3]; // array of 3 register indices int24 imm_int; // immediate integer int line; // line number for trace purposes int trace; // whether to trace }; // (line is not in actual architecture) enum tag_t {INST, DATA}; struct word // word is what can be stored in an address: { union { instruction inst; // an instruction, data Data; // or data } contents; tag_t tag; }; int TRAC; // global indicating trace wanted int nogo; // 1 if error encounterd int linecount; // line count for input errors int input_status = INPUT_READY; // status of input channel int output_status = OUTPUT_READY; // status of output channel int input_word; // input buffer int output_word; // output buffer #define trace(Array, Flag, Index, Value) \ if( TRAC && Flag ) std::cout << " " << #Array << "[" << (Index) << "] = " << (Value) << " "; void error_termination(std::string msg, std::string str); // prototypes void iotrace(std::string string); void reg_error(long i, long Elements) { std::cerr << "*** isc runtime: register index out of range: " << i << std::endl; exit(1); } class processor { public: processor(int n_registers, array & Mem) // processor constructor : mem(Mem), reg(n_registers, reg_error) {} void run(address addr); // start processor at address address show_contents(address ip); // show contents (for listing, tracing) void show_reg(word Word, int r); // show register in instruction void listing(address start, address stop); // generate code listing private: array & mem; // memory safe_array reg; // declare the registers address IP; // instruction pointer instruction IR; // instruction register address MAR; // memory address register word MDR; // memory data register data ALU_in[2]; // ALU input registers data ALU_out; // ALU output register void fetch_and_execute(); void fetch_instruction(); void use_ALU(opcode op); int condition_ALU(opcode op); void read_mem(int TRAC); void write_mem(int TRAC); int show_cont; // whether to show register contents }; void processor::read_mem(int TRAC) { switch( MAR ) // dispatch on memory address contents { case INPUT_WORD: // get input word if( input_status != INPUT_READY ) std::cerr << "*** warning: reading input when device not ready\n"; MDR.contents.Data = input_word; if( TRAC ) std::cout << std::endl << " reading from input word value " << MDR.contents.Data << std::endl; break; case INPUT_STATUS: // get status MDR.contents.Data = input_status; if( TRAC ) std::cout << std::endl << " reading from input status value " << MDR.contents.Data << std::endl; break; case OUTPUT_WORD: // dummy MDR.contents.Data = 0; std::cerr << "*** warning: reading from output word, result always 0\n"; break; case OUTPUT_STATUS: MDR.contents.Data = output_status; if( TRAC ) std::cout << std::endl << " reading from output status value " << MDR.contents.Data << std::endl; break; default: if( MAR < 0 || MAR >= MEMORY ) { std::cerr << "*** isc memory write to invalid location: " << MAR << std::endl; exit(1); } MDR = mem[MAR]; } } void processor::write_mem(int Flag) { trace(mem, Flag, MAR, MDR.contents.Data); switch( MAR ) // dispatch on memory address contents { case INPUT_STATUS: // setting input status to value being written if( Flag ) std::cout << std::endl << " writing to input status value " << MDR.contents.Data << std::endl;; switch( MDR.contents.Data ) { case INPUT_BUSY: // setting to busy (start input) switch( input_status) { case INPUT_READY: iotrace(" input "); if( std::cin >> input_word ) { if( Flag ) std::cout << input_word << std::endl; input_status = INPUT_READY; } else { input_status = END_OF_FILE; if( Flag ) std::cout << "end-of-file\n"; } iotrace("-------"); break; default: std::cerr << "*** warning: setting input_status while device busy\n"; } break; default: std::cerr << "*** warning: should only be setting input_status to " << INPUT_BUSY << std::endl; } break; case INPUT_WORD: if( Flag ) std::cout << std::endl << " writing to input word value " << MDR.contents.Data << std::endl; input_word = MDR.contents.Data; break; case OUTPUT_STATUS: if( Flag ) std::cout << std::endl << " writing to output status value " << MDR.contents.Data << std::endl; switch( MDR.contents.Data ) // setting output status to value being written { case OUTPUT_BUSY: switch( output_status ) { case OUTPUT_READY: output_status = OUTPUT_BUSY; iotrace(" output "); std::cout << output_word << std::endl; iotrace("-------"); output_status = OUTPUT_READY; break; default: std::cerr << "*** warning: setting output_status when busy" << OUTPUT_BUSY << std::endl; } break; default: std::cerr << "*** warning: should only be setting output_status to " << OUTPUT_BUSY << std::endl; } case OUTPUT_WORD: if( output_status != OUTPUT_READY ) std::cerr << "*** warning: wrote to output_word when output not ready\n"; output_word = MDR.contents.Data; break; default: if( MAR < 0 || MAR >= MEMORY ) { std::cerr << "*** isc memory read from invalid location: " << MAR << std::endl; exit(1); } if( mem[MAR].tag == INST ) { std::cerr << "*** isc runtime warning: writing into instruction at location: " << MAR << std::endl; } mem[MAR] = MDR; } } // fetch_instruction fetches next instruction from memory void processor::fetch_instruction() { MAR = IP; // load memory address with instruction location IP++; // increment instruction pointer read_mem(0); // read memory, to get instruction into MDR IR = MDR.contents.inst; // load instruction register with instruction } // use_ALU carries out ALU instruction, assuming data in ALU's input registers void processor::use_ALU(opcode op) { switch( op ) { case ADD: ALU_out = ALU_in[0] + ALU_in[1]; break; case SUB: ALU_out = ALU_in[0] - ALU_in[1]; break; case MUL: ALU_out = ALU_in[0] * ALU_in[1]; break; case DIV: ALU_out = ALU_in[0] / ALU_in[1]; break; case COPY: ALU_out = ALU_in[0]; break; case SHR: if( ALU_in[1] >= 0 ) ALU_out = ALU_in[0] >> ALU_in[1]; // shift right else ALU_out = ALU_in[0] << ALU_in[1]; // shift left if negative break; case SHL: if( ALU_in[1] >= 0 ) ALU_out = ALU_in[0] << ALU_in[1]; // shift left else ALU_out = ALU_in[0] >> ALU_in[1]; // shift right if negative break; case AND: ALU_out = ALU_in[0] & ALU_in[1]; break; case OR: ALU_out = ALU_in[0] | ALU_in[1]; break; case COMP: ALU_out = ~ALU_in[0]; break; default: std::cerr << "internal coding error, unrecognized ALU operation" << op << std::endl; } } // condition_ALU returns a condition bit for the various ALU register tests int processor::condition_ALU(opcode op) { switch( op ) { case JEQ: return ALU_in[0] == ALU_in[1]; case JNE: return ALU_in[0] != ALU_in[1]; case JGT: return ALU_in[0] > ALU_in[1]; case JGTE: return ALU_in[0] >= ALU_in[1]; case JLT: return ALU_in[0] < ALU_in[1]; case JLTE: return ALU_in[0] <= ALU_in[1]; } return 0; } // fetch_and_execute carries out a complete processor cycle // It calls fetch_instruction to get an instruction and partially decodes // the instruction. If the instruction involves the ALU, it // loads the ALU operand registers, then transfers the result to a register // or modifies the IP in the case of a jump. void processor::fetch_and_execute() { fetch_instruction(); // get the instruction switch( IR.op ) // dispatch on operation { case LOAD: /* reg[R0] = mem[reg[R1]]; */ MAR = reg[IR.rix[1]]; read_mem(TRAC && IR.trace); if( TRAC && IR.trace ) std::cout << " reading from location " << MAR << " value " << MDR.contents.Data; trace(reg, IR.trace, IR.rix[0], MDR.contents.Data); reg[IR.rix[0]] = MDR.contents.Data; break; case STORE: /* mem[reg[R0]] = reg[R1]; */ MDR.tag = DATA; MDR.contents.Data = reg[IR.rix[1]]; MAR = reg[IR.rix[0]]; write_mem(TRAC && IR.trace); break; case ADD: /* reg[R0] = reg[R1] + reg[R2]; */ case SUB: /* reg[R0] = reg[R1] - reg[R2]; */ case MUL: /* reg[R0] = reg[R1] * reg[R2]; */ case DIV: /* reg[R0] = reg[R1] / reg[R2]; */ case SHR: /* reg[R0] = reg[R1] >> reg[R2]; */ case SHL: /* reg[R0] = reg[R1] << reg[R2]; */ case AND: /* reg[R0] = reg[R1] && reg[R2]; */ case OR: /* reg[R0] = reg[R1] || reg[R2]; */ ALU_in[1] = reg[IR.rix[2]]; case COPY: /* reg[R0] = reg[R1] */ case COMP: /* reg[R0] = ~reg[R1] */ ALU_in[0] = reg[IR.rix[1]]; use_ALU(IR.op); trace(reg, IR.trace, IR.rix[0], ALU_out); reg[IR.rix[0]] = ALU_out; break; case JEQ: /* jump to reg[R0] if reg[R1] == reg[R2] */ case JNE: /* jump to reg[R0] if reg[R1] != reg[R2] */ case JGT: /* jump to reg[R0] if reg[R1] > reg[R2] */ case JGTE: /* jump to reg[R0] if reg[R1] >= reg[R2] */ case JLT: /* jump to reg[R0] if reg[R1] < reg[R2] */ case JLTE: /* jump to reg[R0] if reg[R1] <= reg[R2] */ ALU_in[0] = reg[IR.rix[1]]; ALU_in[1] = reg[IR.rix[2]]; if( TRAC && IR.trace ) std::cout << " jump to " << reg[IR.rix[0]]; if( condition_ALU(IR.op) ) /* test condition line */ IP = reg[IR.rix[0]]; /* jump to address in R0 */ if( TRAC && IR.trace ) std::cout << (condition_ALU(IR.op) ? "" : " not") << " taken"; break; case JSUB: /* jump to subroutine at reg[R0], IP to reg[R1] */ { trace(reg, IR.trace, IR.rix[1], IP); address temp = IP; /* return address */ IP = reg[IR.rix[0]]; /* jump to address in R0 */ reg[IR.rix[1]] = temp; /* save address in R1 */ if( TRAC && IR.trace ) std::cout << " jumping to sub at " << IP << " return is " << reg[IR.rix[1]]; } break; case JUNC: /* jump unconditionally to contents of reg[R0] */ IP = reg[IR.rix[0]]; /* jump to address in R0 */ if( TRAC && IR.trace ) std::cout << " jumping unconditionally to " << IP; break; case LIM: /* reg[R0] = immediate field in instruction */ ALU_in[0] = IR.imm_int; use_ALU(COPY); reg[IR.rix[0]] = ALU_out; trace(reg, IR.trace, IR.rix[0], IR.imm_int); break; case AIM: /* reg[R0] = reg[R0] + immediate field in instruction */ ALU_in[0] = reg[IR.rix[0]]; ALU_in[1] = IR.imm_int; use_ALU(ADD); reg[IR.rix[0]] = ALU_out; trace(reg, IR.trace, IR.rix[0], ALU_out); break; default: error_termination("Unimplemented instruction", " "); } if( TRAC && IR.trace ) std::cout << std::endl; } void processor::run(address addr) { IP = addr; show_cont = 1; if( TRAC ) std::cout << " Begin trace\n"; do { if( TRAC && mem[IP].contents.inst.trace ) { show_contents(IP); fetch_and_execute(); std::cout << std::endl; } else fetch_and_execute(); // interpret repeatedly } while( IP > 0 ); // jump to 0 implies halt } void showline() { std::cerr << "On line number " << linecount << " "; } void check_register_validity(reg_index R) // check for validity of register { if( R >= REGISTERS ) { showline(); std::cerr << " ISC register index " << R << " out of range[0:" << REGISTERS-1 << "]\n"; exit(1); } } void processor::show_reg(word Word, int r) // show register in instruction { printf("r%-2d ", Word.contents.inst.rix[r]); if(show_cont) printf("(%d) ", reg[Word.contents.inst.rix[r]]); } address processor::show_contents(address ip) // show contents (for listing, tracing) { word Word; Word = mem[ip]; printf("line:%5d loc:%5d contents: %-7s ", Word.contents.inst.line, ip++, ops.get_source(Word.contents.inst.op).c_str()); // show op switch( Word.contents.inst.op ) { case LIM: /* register + value instruction */ case AIM: show_reg(Word, 0); printf("%12d ", Word.contents.inst.imm_int); // show addr break; case JUNC: /* one-register instruction */ show_reg(Word, 0); break; case LOAD: /* two-register instructions */ case STORE: case COPY: case COMP: case JSUB: show_reg(Word, 0); show_reg(Word, 1); break; case ADD: /* three-register instructions */ case SUB: case MUL: case DIV: case SHR: case SHL: case AND: case OR: case JEQ: case JNE: case JGT: case JGTE: case JLT: case JLTE: show_reg(Word, 0); show_reg(Word, 1); show_reg(Word, 2); break; default: break; } std::cout << std::endl; return ip; } void processor::listing(address start, address stop) // generate code listing { show_cont = 0; std::cout << " ISC code listing\n"; for( address VIP = start; VIP < stop; ) { VIP = show_contents(VIP); } std::cout << " end of listing\n"; } int skip_whitespace(std::istream &codefile) // skip over any whitespace { char c; while( codefile.get(c) ) switch(c) { case '\n': linecount++; case ' ': case '\t': break; // continue with whitespace default: codefile.putback(c); return 1; // put back non-whitespace } return 0; // end of file } void nogo_error(std::string message1, std::string message2) { showline(); std::cerr << message1 << " " << message2 << std::endl; nogo = 1; } int get_token(std::istream &codefile, char *buff) { char ch; char *p; int comment_char = 0; p = buff; if( !skip_whitespace(codefile) ) return 0; // end of file while( codefile.get(ch) ) { switch(ch) { case '\n': linecount++; case ' ': case '\t': *p = '\0'; if( p != buff ) return 1; // got token break; case '/': // comment-handling hackery if( comment_char ) // two successive /'s { *(--p) = '\0'; // terminate token while( codefile.get(ch) && ch != '\n') // move to end of line {} if( ch == '\n' ) linecount++; if( p != buff ) return 1; // got token else { if( !skip_whitespace(codefile) ) return 0; // end of file comment_char = 0; // reset } } else { comment_char = 1; // first '/' *p++ = ch; } break; case '*': if( comment_char ) // two successive /'s { *(--p) = '\0'; // terminate token comment_char = 0; while( codefile.get(ch) ) { if( ch == '\n' ) linecount++; else if( ch == '*' ) { char ch2; if( codefile.get(ch2) ) { if( ch2 == '/' ) // end of comment break; if( ch2 == '\n' ) linecount++; } } } if( p != buff ) return 1; // got token } else { *p++ = ch; } break; default: comment_char = 0; *p++ = ch; } } return 1; } void get_token(std::istream &codefile) { char buff[CBUFFSIZE]; get_token(codefile, buff); } // define possible status outcomes as an enumeration type enum conversion_status {CLEAN_CONVERSION, OUT_OF_RANGE, CONVERSION_ERROR}; conversion_status string_to_long(char *string, long &number) // sets number to long achieved from converting string // The returned value of the function is: // 0: if string can be converted cleanly // 1: if string can be converted, but the result is out of range // 2: if string can't be converted { char *endptr; const int radix = 10; errno = 0; // reset error indicator number = strtol(string, &endptr, radix); // try to convert to number if( string[1] == '\0' && (string[0] == '+' || string[0] == '-') ) return CONVERSION_ERROR; // handle case that strtol doesn't catch if( errno == ERANGE ) // check for range error return OUT_OF_RANGE; if( *endptr != '\0' ) // check for residual string return CONVERSION_ERROR; return CLEAN_CONVERSION; // clean conversion } int get_argument(std::istream &codefile) { int index; long value; char buff[CBUFFSIZE]; get_token(codefile, buff); switch( string_to_long(buff, value) ) { case CLEAN_CONVERSION: break; // value is direct integer in source case OUT_OF_RANGE: showline(); error_termination("Invalid integer:", buff); case CONVERSION_ERROR: index = id.find_source(buff); if( index < 0 ) { nogo_error("Undefined symbol: ", buff); value = 0; // dummy value for now } else value = id.get_target(index); // defined break; } return value; } int get_register(std::istream &codefile) { int index; long value; char buff[CBUFFSIZE]; get_token(codefile, buff); switch( string_to_long(buff, value) ) { case CLEAN_CONVERSION: nogo_error("Using absolute number for register", buff); break; // value is direct integer in source case OUT_OF_RANGE: showline(); error_termination("Invalid integer:", buff); case CONVERSION_ERROR: index = regid.find_source(buff); if( index < 0 ) { nogo_error("Undefined symbol: ", buff); value = 0; // dummy value for now } else value = regid.get_target(index); // defined break; } return value; } void init_op_tables() { // put external form of opcodes into symbol table ops.add("load", LOAD); ops.add("store", STORE); ops.add("copy", COPY); ops.add("add", ADD); ops.add("sub", SUB); ops.add("mul", MUL); ops.add("div", DIV); ops.add("shr", SHR); ops.add("shl", SHL); ops.add("and", AND); ops.add("or", OR); ops.add("comp", COMP); ops.add("jeq", JEQ); ops.add("jne", JNE); ops.add("jgt", JGT); ops.add("jgte", JGTE); ops.add("jlt", JLT); ops.add("jlte", JLTE); ops.add("jsub", JSUB); ops.add("junc", JUNC); ops.add("lim", LIM); ops.add("aim", AIM); pops.add("origin", ORIGIN); pops.add("define", DEFINE); pops.add("register", REGISTER); pops.add("use", USE); pops.add("release", RELEASE); pops.add("trace", TRACE); pops.add("label", LABEL); } int find_free_reg(array ®_used) { for( int i = 0; i < reg_used.length(); i++ ) if( reg_used[i] == 0 ) { reg_used[i] = 1; return i; } nogo_error("not enough registers to allocate", ""); return -1; // error indication } void scan_pseudo_op(short pass, int code_index, std::istream &codefile, array ®_used, address &VIP, int &TRACELOC) { char cbuff[CBUFFSIZE]; char cbuff2[CBUFFSIZE]; long value, value2; // values of id int index; int new_reg; // index of register to be allocated by USE int reg_def; reg_def = 0; switch( pops.get_target(code_index) ) { case ORIGIN: // origin designates to set VIP get_token(codefile, cbuff); switch(string_to_long(cbuff, value)) { case CLEAN_CONVERSION: break; case OUT_OF_RANGE: case CONVERSION_ERROR: showline(); error_termination("Invalid integer:", cbuff); } VIP = value; break; case TRACE: // origin designates to set VIP get_token(codefile, cbuff); switch(string_to_long(cbuff, value)) { case CLEAN_CONVERSION: break; case OUT_OF_RANGE: case CONVERSION_ERROR: showline(); error_termination("Invalid integer:", cbuff); } TRACELOC = value; break; case REGISTER: // defining register identifier reg_def = 1; case DEFINE: get_token(codefile, cbuff); // identifier being defined switch(string_to_long(cbuff, value)) { case CLEAN_CONVERSION: case OUT_OF_RANGE: showline(); error_termination("Cannot redefine integer:", cbuff); case CONVERSION_ERROR: break; // symbol, as desired } get_token(codefile, cbuff2); // symbol's value switch(string_to_long(cbuff2, value2)) { case CLEAN_CONVERSION: break; case OUT_OF_RANGE: case CONVERSION_ERROR: showline(); error_termination("Invalid integer:", cbuff); } index = id.find_source(cbuff); if( index >= 0 ) // identifer already seen? { if( id.defined(index) > 0 ) { if( reg_def ) { if( reg_used[value2] ) { std::cerr << "register " << value2 << " to which " << cbuff << " is being defined is already in use " << std::endl; nogo = 1; } else reg_used[value2] = 1; } else if( pass == 1 ) { nogo_error("Multiply defined:", cbuff); } } (reg_def ? regid : id).undefine(index); (reg_def ? regid : id).set_target(index, value2); // set the value } else { (reg_def ? regid : id).add(cbuff, value2); if( reg_def ) reg_used[value2] = 1; } break; case USE: get_token(codefile, cbuff); index = regid.find_source(cbuff); if( index < 0 ) // not used before { new_reg = find_free_reg(reg_used); if( new_reg >= 0 ) regid.add(cbuff, new_reg); // allocate register } else { if( regid.defined(index) > 0 && reg_used[regid.get_target(index)] // in use now ) { nogo_error("Register already in use:", cbuff); } else { new_reg = find_free_reg(reg_used); if( new_reg >= 0 ) regid.set_target(index, new_reg); // allocate register } } break; case RELEASE: get_token(codefile, cbuff); index = regid.find_source(cbuff); if( index < 0 ) // not used before { nogo_error("Can't release register not yet used: ", cbuff); } else { if( reg_used[regid.get_target(index)] ) // in use now { reg_used[regid.get_target(index)] = 0; // take out regid.undefine(index); } else { nogo_error("Can't release register not in use:", cbuff); } } break; case LABEL: get_token(codefile, cbuff); index = id.find_source(cbuff); if( index >= 0 ) // identifer already seen? { if( id.defined(index) > 0 ) { if( pass == 1 ) { nogo_error(" Multiply defined:", cbuff); } id.undefine(index); } id.set_target(index, VIP); } else { id.add(cbuff, VIP); } break; } } address scan(std::istream & codefile) // scan file to form symbol table { char cbuff[CBUFFSIZE]; // buffer for op codes char cbuff2[CBUFFSIZE]; // buffer for op codes int ibuff; // buffer for int int code_index; // opcode index or -1 linecount = 0; address VIP = 0; // Virtual Instruction Pointer int TRACELOC = 1; // whether to trace location array reg_used(REGISTERS); // array indicating registers in use int i; for( i = 0; i < REGISTERS; i++ ) reg_used[i] = 0; // registers not used while( get_token(codefile, cbuff) ) // read until end-of-file { code_index = pops.find_source(cbuff); // check if pseudo-op if( code_index >= 0 ) { scan_pseudo_op(1, code_index, codefile, reg_used, VIP, TRACELOC); } else { code_index = ops.find_source(cbuff); // check if opcode if( code_index < 0 ) { showline(); error_termination("Invalid ISC Opcode: ", cbuff); } switch( ops.get_target(code_index) ) { case JUNC: /* one-register instructions */ get_token(codefile); break; case LOAD: /* two-register instructions */ case STORE: case COPY: case COMP: case JSUB: get_token(codefile); get_token(codefile); break; case ADD: /* three-register instructions */ case SUB: case MUL: case DIV: case SHR: case SHL: case AND: case OR: case JEQ: case JNE: case JGT: case JGTE: case JLT: case JLTE: get_token(codefile); get_token(codefile); get_token(codefile); break; case LIM: /* one-register, one-datum instruction */ case AIM: get_token(codefile); get_token(codefile); // note any symbolic arguments break; default: std::cerr << "internal coding error, unrecognized instruction case" << code_index << std::endl; } VIP++; } } return VIP--; } address load(std::istream & codefile, array & mem) // load code from named stream { char cbuff[CBUFFSIZE]; // buffer for op codes int value; // buffer for int int code_index; // opcode or -1 word Word; // word to be constructed address VIP = 0; // Virtual Instruction Pointer linecount = 0; int TRACELOC = 1; Word.tag = INST; // for now, only load instructions array reg_used(REGISTERS); // array indicating registers in use int i; for( i = 0; i < REGISTERS; i++ ) reg_used[i] = 0; // registers not used while( get_token(codefile, cbuff) ) // read until end-of-file { code_index = pops.find_source(cbuff); if( code_index >= 0 ) { scan_pseudo_op(2, code_index, codefile, reg_used, VIP, TRACELOC); } else { code_index = ops.find_source(cbuff); // check if opcode if( code_index < 0 ) { showline(); error_termination("invalid ISC Opcode: ", cbuff); } Word.contents.inst.trace = TRACELOC; Word.contents.inst.line = linecount+1; // keep line for trace Word.contents.inst.op = opcodes[ops.get_target(code_index)]; // put op in inst word switch( ops.get_target(code_index) ) { case JUNC: /* two-register instructions */ value = get_register(codefile); check_register_validity(value); Word.contents.inst.rix[0] = value; Word.contents.inst.rix[1] = 0; Word.contents.inst.rix[2] = 0; break; case LOAD: /* two-register instructions */ case STORE: case COPY: case COMP: case JSUB: value = get_register(codefile); check_register_validity(value); Word.contents.inst.rix[0] = value; value = get_register(codefile); check_register_validity(value); Word.contents.inst.rix[1] = value; Word.contents.inst.rix[2] = 0; break; case ADD: /* three-register instructions */ case SUB: case MUL: case DIV: case SHR: case SHL: case AND: case OR: case JEQ: case JNE: case JGT: case JGTE: case JLT: case JLTE: value = get_register(codefile); check_register_validity(value); Word.contents.inst.rix[0] = value; value = get_register(codefile); check_register_validity(value); Word.contents.inst.rix[1] = value; value = get_register(codefile); check_register_validity(value); Word.contents.inst.rix[2] = value; break; case LIM: // case using R0 and addr case AIM: value = get_register(codefile); check_register_validity(value); Word.contents.inst.rix[0] = value; Word.contents.inst.rix[1] = 0; Word.contents.inst.rix[2] = 0; value = get_argument(codefile); if( value > MAX_IMMEDIATE || value < MIN_IMMEDIATE ) error_termination( "immediate operand exceeds 24 bits: ", cbuff); Word.contents.inst.imm_int = value; // get integer constant break; default: std::cerr << "internal coding error, unrecognized instruction case " << code_index << std::endl; break; } mem[VIP++] = Word; } } return VIP--; } int occurs(char c, char *str) // check if c occurs in str { for( ; *str ; str++ ) if( c == *str ) return 1; return 0; } void error_termination(std::string msg, std::string str) { std::cerr << "Error termination, " << msg << " " << str << std::endl; exit(1); } std::ostream & operator<<(std::ostream & out, word w) // overload << to accomodate word type { out << w.contents.Data; return out; } // for tracing purposes void iotrace(std::string string) { if( TRAC ) std::cout << "-------" << string << "------" << std::endl; } main(int argc, char **argv) // args are possible flag string { // and mandatory filename if( argc < 2 || argc > 4) { std::cerr << "Usage: isc [-] [lstr ] \n"; exit(1); } if( argc == 4 && occurs('r', argv[1]) ) // number of registers specified { REGISTERS = atoi(argv[argc-2]); } array mem(MEMORY); // declare the memory processor p(REGISTERS, mem); // declare a processor address top; init_op_tables(); char *&filename = argv[argc-1]; std::ifstream codefile(filename); // input file if( !codefile ) { std::cerr << "Code file " << filename << " not found\n"; exit(1); } for( int i = 0; i < MEMORY; i++ ) // initialize memory { mem[i].tag = DATA; mem[i].contents.Data = 0; } nogo = 0; // assembler error flag scan(codefile); codefile.close(); if( argc > 2 && occurs('s', argv[1]) ) // symbol table option { std::cout << " ISCAL Symbol Table\n"; for( int i = 0; i < id.number(); i++ ) { printf("%-30s", id.get_source(i).c_str()); if( id.defined(i) < 1 ) { std::cout << "currently undefined symbol\n"; // could be ok, e.g. released register } else printf("%16d\n", id.get_target(i)); } std::cout << std::endl << " Register Usage\n"; for( int i = 0; i < regid.number(); i++ ) { printf("%-30s", regid.get_source(i).c_str()); if( regid.defined(i) < 1 ) { std::cout << "currently undefined symbol\n"; // could be ok, e.g. released register } else printf("%16d\n", regid.get_target(i)); } } codefile.open(filename); codefile.clear(); top = load(codefile, mem); if( argc > 2 && occurs('l', argv[1]) ) // listing option? p.listing(0, top); if( nogo ) { std::cerr << "errors in source -- nogo\n"; exit(1); } TRAC = ( argc > 2 && occurs('t', argv[1]) ); // trace option? p.run(0); }