diff options
Diffstat (limited to 'zpu/sw/simulator/com/zylin/zpu/simulator/Simulator.java')
-rw-r--r-- | zpu/sw/simulator/com/zylin/zpu/simulator/Simulator.java | 2063 |
1 files changed, 2063 insertions, 0 deletions
diff --git a/zpu/sw/simulator/com/zylin/zpu/simulator/Simulator.java b/zpu/sw/simulator/com/zylin/zpu/simulator/Simulator.java new file mode 100644 index 0000000..c1b86d4 --- /dev/null +++ b/zpu/sw/simulator/com/zylin/zpu/simulator/Simulator.java @@ -0,0 +1,2063 @@ +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() + { + 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 (16*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))); + } + + + + + +} |