package com.zylin.zpu.simulator;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import com.zylin.zpu.simulator.FileTracer.Trace;
import com.zylin.zpu.simulator.exceptions.CPUException;
import com.zylin.zpu.simulator.exceptions.DebuggerBreakpointException;
import com.zylin.zpu.simulator.exceptions.EndSessionException;
import com.zylin.zpu.simulator.exceptions.GDBServerException;
import com.zylin.zpu.simulator.exceptions.HardwareWatchPointException;
import com.zylin.zpu.simulator.exceptions.IllegalInstructionException;
import com.zylin.zpu.simulator.exceptions.InterruptException;
import com.zylin.zpu.simulator.exceptions.MemoryAccessException;
public class Simulator implements ZPU, Machine, Sim
{
int minStack;
/**
* the feeble version of the CPU, e.g. only implements
* 11 instructions.
*
* For debugging purposes it is useful to enable/disable
* each instruction
*/
boolean feeble[]=new boolean[256];
private long opcodeHistogram[]=new long[256];
private long opcodeHistogramCycles[]=new long[256];
private long opcodePairHistogram[]=new long[256*256];
private long opcodePairHistogramCycles[]=new long[256*256];
/** weee! constants are 32 bit by default, so we need to assign a 64 bit
* integer in this matter.
*/
private static final long INTMASK = Long.parseLong("ffffffff", 16);
final static int PUSHPC=59;
final static int OR=7;
final static int NOT=9;
final static int LOAD=8;
final static int STORE=12;
final static int POPPC=4;
final static int FLIP=10;
final static int ADD=5;
final static int PUSHSP=2;
final static int POPSP=13;
final static int NOP=11;
final static int AND=6;
final static int ADDSP=16;
final static int EMULATE=32;
final static int LOADH=34;
final static int STOREH=35;
final static int LESSTHAN=36;
final static int LESSTHANOREQUAL=37;
final static int ULESSTHAN=38;
final static int ULESSTHANOREQUAL=39;
final static int SWAP=40;
final static int MULT=41;
final static int LSHIFTRIGHT=42;
final static int ASHIFTLEFT=43;
final static int ASHIFTRIGHT=44;
final static int CALL=45;
final static int EQ=46;
final static int NEQ=47;
final static int NEG=48;
final static int SUB=49;
final static int XOR=50;
final static int LOADB=51;
final static int STOREB=52;
final static int DIV=53;
final static int MOD=54;
final static int EQBRANCH=55;
final static int NEQBRANCH=56;
final static int POPPCREL=57;
final static int CONFIG=58;
final static int SYSCALL=60;
final static int PUSHSPADD=61;
final static int MULT16X16=62;
final static int CALLPCREL=63;
final static int STORESP=64;
final static int LOADSP=64+32;
int[] memory;
boolean[] validMemory;
protected long cycles;
protected int instructionCount;
private int sp;
private int pc;
protected boolean breakNext;
/* halting synchronization object */
protected Object halt = new Object();
private int IOSIZE=getIOSIZE();
protected int getIOSIZE()
{
return 32768;
}
long prevCycles;
private static final int VECTORSIZE = 0x20;
private static final int VECTOR_RESET = 0;
private static final int VECTOR_INTERRUPT = 1;
private boolean hitVector;
private static final int VECTORBASE = 0x0;
private int nextVector;
protected long lastTimer;
protected boolean timer;
private boolean powerdown;
private boolean decodeMask;
private static final int ZETA = 1;
private static final int ABEL = 0;
private int startStack;
protected Host syscall;
private long[] emulateOpcodeHistogram= new long[256];
private long[] emulateOpcodeHistogramCycles=new long[256];
private long emulateCycles;;
public Simulator() throws CPUException
{
}
public void run() throws CPUException
{
syscall.running();
try
{
instructionLoop();
} catch (EndSessionException e)
{
/* done */
} finally
{
}
dumpInfo();
System.err.println("Stack usage: " + (startStack-minStack));
}
private void dumpInfo()
{
dumpOpcodeHistogram();
//printMemoryHistorgram();
}
private void dumpOpcodeHistogram()
{
System.out.println("Opcode histogram");
dumpHistogram(opcodeHistogram, opcodeHistogramCycles);
System.out.println("Emulate histogram");
dumpHistogram(emulateOpcodeHistogram, emulateOpcodeHistogramCycles);
System.out.println("Pair histogram");
dumpHistogram(opcodePairHistogram, opcodePairHistogramCycles);
dumpGmon();
System.out.println("Grouping of LOADSP/STORESP/IM");
printRange(64, 96);
printRange(96, 128);
printRange(128, 256);
// printRange(64, 65);
// printRange(65, 66);
// printRange(66, 64+32);
// printRange(96, 97);
// printRange(97, 98);
// printRange(98, 96+32);
// printRange(128, 129);
// printRange(129, 130);
// printRange(130, 131);
// printRange(131, 132);
// printRange(132, 133);
// printRange(252, 253);
// printRange(253, 254);
// printRange(254, 255);
// printRange(255, 256);
}
// #define GMON_MAGIC "gmon" /* magic cookie */
// #define GMON_VERSION 1 /* version number */
//
// /*
// * Raw header as it appears on file (without padding):
// */
// struct gmon_hdr
// {
// char cookie[4];
// char version[4]; // a cyg_uint32, target-side endianness
// char spare[3 * 4];
// };
//
// /* types of records in this file: */
// typedef enum
// {
// GMON_TAG_TIME_HIST = 0, GMON_TAG_CG_ARC = 1, GMON_TAG_BB_COUNT = 2
// }
// GMON_Record_Tag;
//
// /* The histogram tag is followed by this header, and then an array of */
// /* cyg_uint16's for the actual counts. */
//
// struct gmon_hist_hdr
// {
// /* host-side gprof adapts to sizeof(void*) and endianness. */
// /* It is assumed that the compiler does not insert padding around the */
// /* cyg_uint32's or the char arrays. */
// void* low_pc; /* base pc address of sample buffer */
// void* high_pc; /* max pc address of sampled buffer */
// cyg_uint32 hist_size; /* size of sample buffer */
// cyg_uint32 prof_rate; /* profiling clock rate */
// char dimen[15]; /* phys. dim., usually "seconds" */
// char dimen_abbrev; /* usually 's' for "seconds" */
// };
//
// /* An arc tag is followed by a single arc record. self_pc corresponds to */
// /* the location of an mcount() call, at the start of a function. from_pc */
// /* corresponds to the return address, i.e. where the function was called */
// /* from. count is the number of calls. */
//
// struct gmon_cg_arc_record
// {
// void* from_pc; /* address within caller's body */
// void* self_pc; /* address within callee's body */
// cyg_uint32 count; /* number of arc traversals */
// };
//
// /* In theory gprof can also process basic block counts, as per the */
// /* compiler's -fprofile-arcs flag. The compiler-generated basic block */
// /* structure should contain a table of addresses and a table of counts, */
// /* and the compiled code updates those counts. Current versions of the */
// /* compiler (~3.2.1) do not output the table of addresses, and without */
// /* that table gprof cannot process the counts. Possibly gprof should read */
// /* in the .bb and .bbg files generated for gcov processing, but that does */
// /* not happen at the moment. */
// /* */
// /* So for now gmon.out does not contain basic block counts and gprof */
// /* operations that depend on it, e.g. --annotated-source, won't work. */
/**
* Write gmon.out file.
**/
private void dumpGmon()
{
if (memory==null)
return;
try
{
ByteArrayOutputStream b=new ByteArrayOutputStream();
// /*
// * Raw header as it appears on file (without padding):
// */
// struct gmon_hdr
// {
// char cookie[4];
// char version[4]; // a cyg_uint32, target-side endianness
// char spare[3 * 4];
// };
// #define GMON_MAGIC "gmon" /* magic cookie */
// #define GMON_VERSION 1 /* version number */
// dump binary memory gmon.out &profile_gmon_hdr ((char*)&profile_gmon_hdr + sizeof(struct gmon_hdr))
b.write("gmon".getBytes());
writeLong(b, 1); // version
b.write(new byte[3*4]); // spare
// GMON_TAG_TIME_HIST = 0, GMON_TAG_CG_ARC = 1, GMON_TAG_BB_COUNT = 2
// append binary memory gmon.out &profile_tags[0] &profile_tags[1]
b.write(new byte[]{0}); // GMON_TAG_TIME_HIST
//
// // The gprof documentation claims that this should be the size in
// // bytes. The implementation treats it as a count.
// profile_hist_hdr.hist_size = (cyg_uint32) ((text_size + bucket_size - 1) / bucket_size);
// profile_hist_hdr.low_pc = _start;
// profile_hist_hdr.high_pc = (void*)((cyg_uint8*)_end - 1);
// // The prof_rate is the frequency in hz. The resolution argument is
// // an interval in microseconds.
// profile_hist_hdr.prof_rate = 1000000 / resolution;
//
// // Now allocate a buffer for the histogram data.
// profile_hist_data = (cyg_uint16*) malloc(profile_hist_hdr.hist_size * sizeof(cyg_uint16));
// if ((cyg_uint16*)0 == profile_hist_data) {
// diag_printf("profile_on(): cannot allocate histogram buffer - ignored\n");
// return;
// }
// memset(profile_hist_data, 0, profile_hist_hdr.hist_size * sizeof(cyg_uint16));
// struct gmon_hist_hdr
// {
// /* host-side gprof adapts to sizeof(void*) and endianness. */
// /* It is assumed that the compiler does not insert padding around the */
// /* cyg_uint32's or the char arrays. */
// void* low_pc; /* base pc address of sample buffer */
// void* high_pc; /* max pc address of sampled buffer */
// cyg_uint32 hist_size; /* size of sample buffer */
// cyg_uint32 prof_rate; /* profiling clock rate */
// char dimen[15]; /* phys. dim., usually "seconds" */
// char dimen_abbrev; /* usually 's' for "seconds" */
// };
// maximum 65536 buckets.
int length=memory.length*4;
if (length > 60000)
{
length=60000;
}
int buckets[]=new int[length];
for (long i=0; i<profile.length;i++)
{
buckets[(int)((i*(((long)buckets.length)-1))/(((long)profile.length)-1))]+=profile[(int)i];
}
// append binary memory gmon.out &profile_hist_hdr ((char*)&profile_hist_hdr + sizeof(struct gmon_hist_hdr))
writeLong(b, 0); // low_pc
writeLong(b, memory.length*4); // high_pc
writeLong(b, buckets.length); // # of samples
writeLong(b, 64000000); // 64MHz
b.write("seconds".getBytes());
b.write(new byte[15-"seconds".length()]);
b.write("s".getBytes());
// append binary memory gmon.out profile_hist_data (profile_hist_data + profile_hist_hdr.hist_size)
for (int i=0; i<buckets.length;i++)
{
int val;
val=buckets[i];
if (val>65535)
{
val=65535;
}
writeShort(b, val);
}
OutputStream o=new FileOutputStream("gmon.out");
b.writeTo(o);
o.flush();
o.close();
} catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void writeLong(ByteArrayOutputStream b, int i) throws IOException
{
int val=i;
b.write(new byte[]{(byte)((val>>24)&0xff),
(byte)((val>>16)&0xff),
(byte)((val>>8)&0xff),
(byte)((val>>0)&0xff)});
}
private void writeShort(ByteArrayOutputStream b, int i) throws IOException
{
int val=i;
b.write(new byte[]{ (byte)((val>>8)&0xff),
(byte)((val>>0)&0xff)});
}
private void dumpHistogram(long[] ms, long[] ms2)
{
List<OpcodeSample> l=new LinkedList();
totalCycles = 0;
for (int i=0; i<256; i++)
{
totalCycles+=opcodeHistogramCycles[i];
}
for (int i=0; i<ms.length; i++)
{
final int j=i;
l.add(new OpcodeSample(j, ms2[j]));
}
Collections.sort(l, new Comparator()
{
public int compare(Object arg0, Object arg1)
{
OpcodeSample a=(OpcodeSample) arg0, b=(OpcodeSample) arg1;
if (a.count<b.count)
{
return 1;
} else if (a.count==b.count)
{
return 0;
} else
{
return -1;
}
}
});
for (int i=0; i<ms.length; i++)
{
if (totalCycles==0)
break;
double d = ((double)l.get(i).count/((double)totalCycles));
if (d<0.005)
break;
double cycPerIns = ((double)ms2[l.get(i).j]/((double)ms[l.get(i).j]));
System.out.println("0x"+ Integer.toHexString(l.get(i).j) + " " + d + " " + l.get(i).count + " " + cycPerIns );
}
}
private void printRange(int from, int to)
{
int totalLoadSP=0;
for (int i=from; i<to; i++)
{
totalLoadSP+=opcodeHistogram[i];
}
double d = ((double)totalLoadSP/((double)totalCycles));
System.out.println(""+ from + " " + d + " " + totalLoadSP);
}
// private void printMemoryHistorgram()
// {
// Arrays.sort(profile, new Comparator()
// {
// public int compare(Object o1, Object o2)
// {
// return (int)(((Profile)o2).counter-
// ((Profile)o1).counter);
// }
// });
// System.err.println("Profiling information");
// for (int i=0; i<1000; i++)
// {
// if (profile[i].counter==0)
// {
// break;
// }
// System.err.println("0x"+Integer.toHexString(profile[i].address)+ " " + profile[i].counter);
// }
// }
/**
* notify everybody that we are powering down
*/
public void shutdown()
{
powerdown=true;
/* wake up */
synchronized(halt)
{
halt.notify();
}
}
/**
* This method can be invoked in two cases:
*
* a) while the CPU is running on the simulator thread
* b) while the CPU is halted from other threads
*/
protected void resetHardwareInternal() throws CPUException
{
interrupt=false;
timer=false;
lastTimer=0;
hitVector=false;
instructionCount=0;
for (int i=0; i<memory.length; i++)
{
memory[i]=0;
}
setPcToVector(VECTOR_RESET); // starting address
startStack=getStartStack();
minStack=startStack;
changeSp(startStack);
intSp=0;
}
private void instructionLoop() throws EndSessionException, CPUException
{
/* wait for connection.... */
for (;;)
{
try
{
/*
* execute an instruction.
*
* If an exception happens while executing the instruction,
* invoke the approperiate exception vector.
*
* If a second exception occurs while invoking the
* exception(i.e. before the first instruction of the vector
* is executed), invoke the reboot exception.
*/
executeInstruction();
} catch (DebuggerBreakpointException e1)
{
suspend();
} catch (InterruptException e1)
{
armVector(VECTOR_INTERRUPT);
} catch (IllegalInstructionException e1)
{
suspend();
} catch (MemoryAccessException e1)
{
suspend();
} catch (CPUException e)
{
suspend();
} catch (GDBServerException e)
{
suspend();
} catch (IOException e)
{
e.printStackTrace();
suspend();
} catch (RuntimeException e)
{
e.printStackTrace();
suspend();
} finally
{
checkCommit();
}
}
}
private void armVector(int vector)
{
// for now we always break as soon as we hit a vector
if (vector!=VECTOR_INTERRUPT)
{
// print(MINIMAL, "Vector " + vector + " armed at PC: " + formatHex(pc, "00000000"));
suspend();
}
hitVector=true;
nextVector=vector;
}
private void checkVector() throws CPUException
{
if (hitVector)
{
hitVector=false;
invokeVector(nextVector);
}
}
private void invokeVector(int vector) throws CPUException
{
push(pc);
setPcToVector(vector);
}
private void setPcToVector(int vector) throws MemoryAccessException
{
setPc(VECTORSIZE*vector+VECTORBASE);
}
private void executeInstruction() throws CPUException, EndSessionException, GDBServerException, IOException
{
for (;;)
{
checkHalt();
/* jump to any armed vector */
checkVector();
checkInterrupts();
tracer.instructionEvent();
commit = false;
savedSp=getSp();
savedPc=pc;
savedDecodeMask=decodeMask;
touchedPc=false;
instruction=cpuReadByte(pc);
// electrons perish each time we attempt an instruction
tick();
if (((instruction&0x80)!=0))
{
int t=((instruction<<(32-7)))>>(32-7);
if (decodeMask)
{
int a;
a=(popIntStack()<<7)|(t&0x7f);
pushIntStack(a);
} else
{
pushIntStack(t);
}
decodeMask=true;
} else
{
decodeMask = false;
if (isAddSP(instruction))
{
int offset=instruction - ADDSP;
int valAddr=sp+offset*4;
int a = popIntStack();
pushIntStack(cpuReadLong(valAddr) + a);
} else if ((instruction >= LOADSP) && (instruction < LOADSP + 32))
{
int addr;
addr = getSp();
int offset=(instruction - LOADSP)^0x10;
addr += 4 * offset;
pushIntStack(cpuReadLong(addr));
} else if (isStoreSP(instruction))
{
int addr;
addr = getSp();
int offset=(instruction - STORESP)^0x10;
addr += 4 * offset;
cpuWriteLong(addr, popIntStack());
} else
{
int addr;
int val;
switch (instruction)
{
case 0:
throw new DebuggerBreakpointException();
case PUSHPC:
pushIntStack(pc);
break;
case OR:
pushIntStack(popIntStack() | popIntStack());
break;
case NOT:
pushIntStack(popIntStack() ^ 0xffffffff);
break;
case LOAD:
pushIntStack(cpuReadLong(popIntStack()));
break;
case PUSHSPADD:
if (feeble[PUSHSPADD])
{
emulate();
} else
{
int a;
int b;
a=sp;
b=popIntStack()*4;
pushIntStack(a+b);
}
break;
case STORE:
addr = popIntOrExt();
val = popIntOrExt();
cpuWriteLong(addr, val);
break;
case POPPC:
{
// NB!!!! does NOT flush internal stack
int a;
if (intSp>0)
{
a=popIntStack();
} else
{
a=pop();
}
if ((sp>=emulateSp)&&(emulateInProgress))
{
emulateInProgress=false;
/* we returned from an emulate instruction */
emulateOpcodeHistogram[emulateOpcode]++;
emulateOpcodeHistogramCycles[emulateOpcode]+=cycles-emulateCycles;
}
setPc(a);
break;
}
case POPPCREL:
if (feeble[POPPCREL])
{
emulate();
} else
{
setPc(popIntStack()+getPc());
}
break;
case FLIP:
pushIntStack(flip(popIntStack()));
break;
case ADD:
pushIntStack(popIntStack() + popIntStack());
break;
case SUB:
if (feeble[SUB])
{
emulate();
} else
{
int a=popIntStack();
int b=popIntStack();
pushIntStack(b-a);
}
break;
case PUSHSP:
pushIntStack(getSp());
break;
case POPSP:
changeSp(popIntStack());
intSp=0; // flush internal stack
break;
case NOP:
break;
case AND:
pushIntStack(popIntStack() & popIntStack());
break;
case XOR:
if (feeble[XOR])
{
emulate();
} else
{
pushIntStack(popIntStack() ^ popIntStack());
}
break;
case LOADB:
if (feeble[LOADB])
{
emulate();
} else
{
pushIntStack(cpuReadByte(popIntStack()));
}
break;
case STOREB:
if (feeble[STOREB])
{
emulate();
} else
{
addr = popIntStack();
val = popIntStack();
cpuWriteByte(addr, val);
}
break;
case LOADH:
if (feeble[LOADH])
{
emulate();
} else
{
pushIntStack(cpuReadWord(popIntStack()));
}
break;
case STOREH:
if (feeble[STOREH])
{
emulate();
} else
{
addr = popIntStack();
val = popIntStack();
cpuWriteWord(addr, val);
}
break;
case LESSTHAN:
if (feeble[LESSTHAN])
{
emulate();
} else
{
int a;
int b;
a = popIntStack();
b = popIntStack();
pushIntStack((a < b) ? 1 : 0);
}
break;
case LESSTHANOREQUAL:
if (feeble[LESSTHANOREQUAL])
{
emulate();
} else
{
int a;
int b;
a = popIntStack();
b = popIntStack();
pushIntStack((a <= b) ? 1 : 0);
}
break;
case ULESSTHAN:
if (feeble[ULESSTHAN])
{
emulate();
} else
{
long a;
long b;
a = ((long) popIntStack()) & INTMASK;
b = ((long) popIntStack()) & INTMASK;
pushIntStack((a < b) ? 1 : 0);
}
break;
case ULESSTHANOREQUAL:
if (feeble[ULESSTHANOREQUAL])
{
emulate();
} else
{
long a;
long b;
a = ((long) popIntStack()) & INTMASK;
b = ((long) popIntStack()) & INTMASK;
pushIntStack((a <= b) ? 1 : 0);
}
break;
case SWAP:
// if (feeble[SWAP])
// {
// emulate();
// } else
{
int swapVal=popIntStack();;
pushIntStack(((swapVal >>16)&0xffff)|(swapVal<<16));
}
break;
case MULT16X16:
// if (feeble[SWAP])
// {
// emulate();
// } else
{
int a=popIntStack();
int b=popIntStack();
pushIntStack((a&0xffff)*(b&0xffff));
}
break;
case EQBRANCH:
if (feeble[EQBRANCH])
{
emulate();
} else
{
int compare;
int target;
target = popIntStack() + pc;
compare = popIntStack();
if (compare == 0)
{
setPc(target);
} else
{
setPc(pc + 1);
}
}
break;
case NEQBRANCH:
if (feeble[NEQBRANCH])
{
emulate();
} else
{
int compare;
int target;
target = popIntStack() + pc;
compare = popIntStack();
if (compare != 0)
{
setPc(target);
} else
{
setPc(pc + 1);
}
}
break;
case MULT:
if (feeble[MULT])
{
emulate();
} else
{
pushIntStack(popIntStack() * popIntStack());
}
break;
case DIV:
if (feeble[DIV])
{
emulate();
} else
{
int a;
int b;
a = popIntStack();
b = popIntStack();
if (b == 0)
{
throw new CPUException();
}
pushIntStack(a / b);
}
break;
case MOD:
if (feeble[MOD])
{
emulate();
} else
{
int a;
int b;
a = popIntStack();
b = popIntStack();
if (b == 0)
{
throw new CPUException();
}
pushIntStack(a % b);
}
break;
case LSHIFTRIGHT:
if (feeble[LSHIFTRIGHT])
{
emulate();
} else
{
long shift;
long valX;
int t;
shift = ((long) popIntStack()) & INTMASK;
valX = ((long) popIntStack()) & INTMASK;
t = (int) (valX >> (shift & 0x3f));
pushIntStack(t);
}
break;
case ASHIFTLEFT:
if (feeble[ASHIFTLEFT])
{
emulate();
} else
{
long shift;
long valX;
shift = ((long) popIntStack()) & INTMASK;
valX = ((long) popIntStack()) & INTMASK;
int t = (int) (valX << (shift & 0x3f));
pushIntStack(t);
}
break;
case ASHIFTRIGHT:
if (feeble[ASHIFTRIGHT])
{
emulate();
} else
{
long shift;
int valX;
shift = ((long) popIntStack()) & INTMASK;
valX = popIntStack();
int t = valX >> (shift & 0x3f);
pushIntStack(t);
}
break;
case CALL:
if (feeble[CALL])
{
emulate();
} else
{
intSp=0; // flush internal stack
int address = pop();
push(pc + 1);
setPc(address);
}
break;
case CALLPCREL:
if (feeble[CALLPCREL])
{
emulate();
} else
{
intSp=0; // flush internal stack
int address = pop();
push(pc + 1);
setPc(address+pc);
}
break;
case EQ:
if (feeble[EQ])
{
emulate();
} else
{
pushIntStack((popIntStack() == popIntStack()) ? 1 : 0);
}
break;
case NEQ:
if (feeble[NEQ])
{
emulate();
} else
{
pushIntStack((popIntStack() != popIntStack()) ? 1 : 0);
}
break;
case NEG:
if (feeble[NEG])
{
emulate();
} else
{
pushIntStack(-popIntStack());
}
break;
case CONFIG:
if (emulateConfig())
{
emulate();
cpu=ABEL;
} else
{
cpu = popIntStack();
}
switch (cpu)
{
case ABEL:
System.err.println("ZPU feeble instruction set");
for (int i = 0; i < feeble.length; i++)
{
feeble[i] = true;
}
setFeeble();
break;
case ZETA:
System.err.println("ZPU full instruction set");
for (int i = 0; i < feeble.length; i++)
{
feeble[i] = false;
}
break;
default:
break;
}
break;
case SYSCALL:
if (feeble[SYSCALL])
{
throw new IllegalInstructionException();
} else
{
intSp=0; // flush internal stack
syscall.syscall(this);
}
break;
default:
throw new IllegalInstructionException();
}
}
}
if (!touchedPc)
{
setPc(pc + 1);
}
committed();
// one more instruction retired
instructionCount++;
}
}
protected void setFeeble()
{
feeble[NEQBRANCH] = false;
feeble[EQ] = false;
feeble[LOADB] = false;
feeble[LESSTHAN] = false;
feeble[ULESSTHAN] = false;
feeble[STOREB] = false;
feeble[MULT] = false;
feeble[CALL] = true;
feeble[POPPCREL] = true;
feeble[LESSTHANOREQUAL] = true;
feeble[ULESSTHANOREQUAL] = true;
feeble[PUSHSPADD] = false;
feeble[CALLPCREL] = false;
feeble[SUB] = false;
}
private int popIntOrExt()
{
int a;
if (intSp==0)
{
a=pop();
} else
{
a=popIntStack();
}
return a;
}
int intSp;
private int emulateSp;
private int emulateOpcode;
private boolean emulateInProgress;
protected boolean timerPending;
private boolean inInterrupt;
private int popIntStack()
{
// if (intSp<=0)
// throw new IllegalInstructionException();
intSp--;
return pop();
}
private void pushIntStack(int x)
{
// if (intSp>=32)
// throw new IllegalInstructionException();
push(x);
intSp++;
}
private static boolean isAddSP(int instruction)
{
return (instruction >= ADDSP) && (instruction < ADDSP + 16);
}
private static boolean isStoreSP(int instruction)
{
return (instruction >= STORESP) && (instruction < STORESP + 32);
}
protected boolean emulateConfig()
{
return false;
}
private void checkCommit() throws CPUException
{
if (!commit)
{
decodeMask=savedDecodeMask;
pc=savedPc;
setSp(savedSp);
committed();
}
}
private void committed()
{
commit=true;
tracer.commit();
}
private void emulate() throws CPUException
{
// NB! Do NOT flush internal stack
// intSp=0; // flush internal stack
/* three total overhead to emulate instruction */
if (!emulateInProgress)
{
emulateInProgress=true;
emulateSp = sp;
emulateOpcode = getOpcode();
emulateCycles = cycles;
}
pushIntStack(pc+1);
setPc((cpuReadByte(pc)-32)*VECTORSIZE+VECTORBASE);
}
private void checkInterrupts() throws InterruptException
{
if (!tracer.simInterrupt())
{
/* These flags are set *regardless* of interrupt state. */
while (lastTimer+timerInterval<cycles)
{
if (timerInterval>0)
{
lastTimer+=timerInterval;
} else
{
lastTimer=cycles;
}
timerPending=true;
}
}
if (!interrupt)
return;
/* if we are in the middle of decoding an instruction, no interrupt */
if (decodeMask)
{
return;
}
if (tracer.simInterrupt())
{
if (!tracer.onInterrupt())
{
inInterrupt=false;
}
if (inInterrupt)
{
return;
}
/* Use trace information instead of trying to figure out when an interrupt happens. We don't try
* to simulate anything more complicated than timer interrupts so we don't need to worry about source.
*/
if (tracer.onInterrupt()&&!inInterrupt)
{
if (!timer)
{
throw new IllegalInstructionException();
}
inInterrupt=true;
timerPending=true;
throw new InterruptException();
}
} else
{
if (!timerPending)
inInterrupt=false;
if (inInterrupt)
{
return;
}
if (timer&&timerPending)
{
inInterrupt=true;
throw new InterruptException();
}
}
}
private void cpuWriteWord(int addr, int val) throws MemoryAccessException
{
if ((addr&0x1)!=0)
{
throw new MemoryAccessException();
}
for (int i=0; i<2; i++)
{
writeByte(addr+i, val>>(8*(1-i)));
}
}
/**
* @param i
* @return
* @throws MemoryAccessException
*/
private int cpuReadWord(int addr) throws MemoryAccessException
{
if ((addr&0x1)!=0)
{
throw new MemoryAccessException();
}
return ((readByteInternal(addr+0)&0xff)<<8) | (readByteInternal(addr+1)&0xff);
}
private void cpuWriteByte(int addr, int val) throws MemoryAccessException
{
writeByte(addr, val);
}
protected boolean interrupt;
protected long timerInterval;
private boolean touchedPc;
private boolean accessWatchPoint;
private int accessWatchPointAddress;
private int accessWatchPointLength;
private boolean commit;
private boolean savedDecodeMask;
private int savedSp;
private int savedPc;
private long[] profile;
private int cpu;
private long sampledCycle;
private Tracer tracer=new Tracer()
{
public void instructionEvent()
{
}
public void commit()
{
}
public void setSp(int sp)
{
}
public void dumpTraceBack()
{
}
public boolean onInterrupt()
{
return false;
}
public boolean simInterrupt()
{
return false;
}
};
private int instruction;
private long totalCycles;
private String traceFileName;
private int prevOpcode;
private long prevCycles2;
private int prevOpcode2;
/**
* checks if the CPU should halt, and halts. Fn. returns when the
* CPU has resumed execution.
* @throws EndSessionException
*/
private void checkHalt() throws EndSessionException
{
synchronized(halt)
{
if (powerdown)
{
throw new EndSessionException();
}
if (breakNext)
{
breakNext=false;
halt.notify();
try
{
syscall.halted();
halt.wait();
syscall.running();
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
if (powerdown)
{
throw new EndSessionException();
}
}
}
private int flip(int i)
{
int t=0;
for (int j=0; j<32; j++)
{
t|=((i>>j)&1)<<(31-j);
}
return t;
}
/** the CPU is writing a long during execution */
public void cpuWriteLong(int addr, int val) throws MemoryAccessException
{
if (accessWatchPoint&&(addr==accessWatchPointAddress))
{
suspend();
}
if ((addr&0x3)!=0)
{
throw new MemoryAccessException();
}
if ((addr>=getIO())&&(addr<getIO()+IOSIZE))
{
ioWrite(addr, val);
} else if ((addr>=0)&&(addr<=memory.length*4))
{
memory[addr/4]=val;
validMemory[addr/4]=true;
} else
{
throw new MemoryAccessException();
}
}
public void writeByte(int addr, int val) throws MemoryAccessException
{
if ((addr>=0)&&(addr<memory.length*4))
{
memory[addr/4]=(memory[addr/4]&(~(0xff<<((3-addr&3)*8))))|((val&0xff)<<((3-addr&3)*8));
} else
{
throw new MemoryAccessException();
}
}
protected void ioWrite(int addr, int val) throws MemoryAccessException
{
addr-=getIO();
/* note, big endian! */
switch (addr)
{
case 12:
syscall.writeUART(val);
break;
case 20:
interrupt=val!=0;
break;
case 28:
timerInterval=val;
break;
case 32:
timer=val!=0;
break;
case 0x24:
syscall.writeUART(val);
break;
case 0x100:
writeTimerSampleReg(val);
break;
default:
break;
}
}
protected void writeTimerSampleReg(int val)
{
if ((val&0x2)!=0)
{
sampledCycle=getSampleOffset(); // we need a fudge factor to make up for differences in when relative to the instruction the data is sampled.
}
}
protected long getSampleOffset()
{
return cycles+2+0xd-(0x8e-0x74);
}
protected int ioRead(int addr) throws CPUException
{
addr-=getIO();
/* note, big endian! */
switch (addr)
{
case 20:
return interrupt?1:0;
case 32:
return timer?1:0;
case 0x24:
return syscall.readUART();
/* FIFO empty? bit 0, FIFO full bit 1(never the case) */
case 0x28:
return syscall.readFIFO();
case 0x100:
case 0x104:
case 0x108:
case 0x10c:
case 0x110:
case 0x114:
case 0x118:
case 0x11c:
return readSampledTimer(addr, 0x100);
case 0x200:
return readMHz();
default:
throw new MemoryAccessException();
}
}
protected int readMHz()
{
/* 90 MHz */
return 100;
}
protected int readSampledTimer(int addr, int base)
{
int t=0;
t=(int)((sampledCycle>>(((addr-base)/4)*32))&0xffffffff);
return t;
}
private int cpuReadByte(int addr) throws MemoryAccessException
{
return readByteInternal(addr);
}
/** this is the CPU reading a long word during execution */
public int cpuReadLong(int addr) throws CPUException
{
if (accessWatchPoint&&(addr==accessWatchPointAddress))
{
suspend();
}
if ((addr&0x3)!=0)
{
throw new MemoryAccessException();
}
if ((addr>=getIO())&&(addr<getIO()+IOSIZE))
{
return ioRead(addr);
} else if ((addr>=0)&&(addr<=memory.length*4))
{
return memory[addr/4];
} else
{
throw new MemoryAccessException();
}
}
/**
* Causes a cycle to pass.
* @throws MemoryAccessException
*/
/** increase time and record how long we spent on this instruction */
private void tick() throws MemoryAccessException
{
profile[pc]++;
int opcode;
opcode=readByte(pc);
opcodeHistogram[prevOpcode]++;
opcodeHistogramCycles[prevOpcode]+=cycles-prevCycles;
int opcodePair=groupOpcode(prevOpcode2)*256+groupOpcode(prevOpcode);
opcodePairHistogram[opcodePair]++;
opcodePairHistogramCycles[opcodePair]+=cycles-prevCycles2;
prevOpcode2=prevOpcode;
prevOpcode=opcode;
prevCycles2=prevCycles;
prevCycles=cycles;
cycles++;
}
private int groupOpcode(int instruction)
{
if (isAddSP(instruction))
{
return ADDSP;
} else if ((instruction >= LOADSP) && (instruction < LOADSP + 32))
{
return LOADSP;
} else if (isStoreSP(instruction))
{
return STORESP;
}
if ((instruction&0x80)!=0)
return 0x80;
return instruction;
}
public int readByte(int addr) throws MemoryAccessException
{
if ((addr>=0)&&(addr<memory.length*4))
{
return readByteInternal(addr);
} else
{
throw new MemoryAccessException();
}
}
protected int readByteInternal(int addr) throws MemoryAccessException
{
return (memory[addr/4]>>((3-addr&0x3)*8))&0xff;
}
private int pop() throws CPUException
{
int val;
validMemory[getSp()/4]=false;
val=cpuReadLong(getSp());
setSp(getSp() + 4);
return val;
}
private void push(int imm) throws CPUException
{
setSp(getSp() - 4);
cpuWriteLong(getSp(), imm);
}
private final class OpcodeSample
{
private final int j;
int opcode;
long count;
private OpcodeSample(int j, long l)
{
this.j = j;
opcode = j;
count = l;
}
}
private void initRam()
{
memory = (new int[getRAMSIZE()/4]);
validMemory = new boolean[getRAMSIZE()/4];
for (int i=0; i<validMemory.length; i++)
{
validMemory[i]=true;
}
profile = new long[getRAMSIZE()];
}
public void setPc(int pc) throws MemoryAccessException
{
if ((pc<VECTORBASE)||(pc>memory.length*4))
{
throw new MemoryAccessException();
}
this.pc = pc;
touchedPc=true;
}
public int getPc()
{
return pc;
}
/** resume execution. This function returns when the CPU halts again. */
public void cont()
{
for (;;)
{
synchronized(halt)
{
halt.notify();
try
{
halt.wait();
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
if (syscall.doneContinue())
{
break;
}
}
}
/** resume execution. This function returns when the CPU halts again. */
public void step()
{
synchronized(halt)
{
suspend();
cont();
}
}
public int getReg(int regNum) throws CPUException
{
if ((regNum>=0)&&(regNum<32))
{
return memory[regNum];
} else if (regNum==32)
{
return getSp();
} else if (regNum==33)
{
return pc;
} else
{
throw new RuntimeException("Illegal getReg()");
}
}
public int getREGNUM()
{
return 34;
}
public long getCycleCounter()
{
return cycles;
}
public void addWaitStates(int num)
{
}
/** tells simulator to enter the suspended state */
public void suspend()
{
synchronized(halt)
{
breakNext=true;
}
// tracer.dumpTraceBack();
}
public long getPrevCycles()
{
return prevCycles;
}
public long getCycles()
{
return cycles;
}
public void enableAccessWatchPoint(int address, int length) throws CPUException
{
if (accessWatchPoint)
{
throw new HardwareWatchPointException();
}
accessWatchPointAddress=address;
accessWatchPointLength=length;
accessWatchPoint=true;
}
public void disableAccessWatchPoint(int address, int length) throws CPUException
{
if (!accessWatchPoint)
{
throw new HardwareWatchPointException();
}
if ((address!=accessWatchPointAddress)||(length!=accessWatchPointLength))
{
throw new HardwareWatchPointException();
}
accessWatchPoint=false;
}
/** POPSP changes the stack pointer */
public void changeSp(int sp) throws CPUException
{
setSp(sp);
tracer.setSp(sp);
}
public void setSp(int sp) throws CPUException
{
if ((sp%4)!=0)
{
throw new IllegalInstructionException();
}
if (sp<minStack)
{
minStack=sp;
}
this.sp = sp;
}
public int getSp()
{
return sp;
}
public int getIntSp()
{
return (intSp+(INTSTACKSIZE-1))%INTSTACKSIZE;
}
protected int getIO()
{
return 0x80000000;
}
protected int getRAMSIZE()
{
return (2*1024*1024);
}
protected int getStartStack()
{
return memory.length*4-0x10000;
}
public void setTraceFile(String string)
{
traceFileName=string;
}
public void setSyscall(Host syscall)
{
this.syscall=syscall;
}
public void loadImage(InputStream inputStream, int length) throws IOException, CPUException
{
if (length==-1)
throw new IOException("File image length not known");
for (int i=0; i<length; i++)
{
int t=inputStream.read();
writeByte(0+i, t);
}
}
public int getArg(int num) throws CPUException
{
return cpuReadLong(getSp()+4+num*4);
}
public int getOpcode() throws MemoryAccessException
{
return readByte(pc);
}
static final int INTSTACKSIZE=32;
public boolean checkMatch(Trace trace)
{
cycles=trace.cycle;
if (!trace.undefinedIntSp)
{
if (trace.intSp!=((intSp+(INTSTACKSIZE-1))%INTSTACKSIZE))
return false;
}
if ((getPc() != trace.pc) || (getSp() != trace.sp)
|| (getOpcode() != trace.opcode))
{
return false;
}
if (cpuReadLong(getSp()) == trace.stackA)
{
if (cpuReadLong(getSp() + 4) == trace.stackB)
{
return true;
}
}
if ((!validMemory[getSp()/4])||cpuReadLong(getSp()) == trace.stackA)
{
if ((!validMemory[(getSp()+4)/4])||cpuReadLong(getSp() + 4) == trace.stackB)
{
// System.out.println("Undefined memory location mismatch");
return true;
}
}
if (!trace.undefinedIntSp)
{
if ((intSp<1)||cpuReadLong(getSp()) == trace.stackA)
{
if ((intSp<2)||cpuReadLong(getSp() + 4) == trace.stackB)
{
return true;
}
}
}
return false;
}
public void sessionStarted()
{
if (traceFileName!=null)
{
tracer = new FileTracer(this, traceFileName);
}
/* Set the feeble flag to enable/disable instructions here */
initRam();
resetHardwareInternal();
}
void printState(FileTracer fileTracer)
{
System.err.println("intSp: " + getIntSp());
System.err.println(Integer.toHexString(getPc())+ " " +
Integer.toHexString(getOpcode()) + " " +
Integer.toHexString(getSp()) + " " +
Integer.toHexString(cpuReadLong(getSp())) + " " +
Integer.toHexString(cpuReadLong(getSp()+4)));
}
}