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