/*
This file is part of JOP, the Java Optimized Processor
see <http://www.jopdesign.com/>
Copyright (C) 2001-2008, Martin Schoeberl (martin@jopdesign.com)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jopdesign.sys;
import com.jopdesign.io.IOFactory;
import com.jopdesign.io.SysDevice;
//
// I don't like to make JVMHelp public, but I need it in java.io.PrintStream
//
// class JVMHelp {
public class JVMHelp {
// don't use static initializers <clinit> in this class as some
// methods depend on the order.
static Runnable ih[][];
static Runnable dh;
// Cannot be used here as <clinit> of the factory depends on this
// class for the helper method. Would result in a cyclic dependency
// of <clinit>.
// static SysDevice sys = IOFactory.getFactory().getSysDevice();
static SysDevice sys;
static StackOverflowError SOError;
static NullPointerException NPExc;
static ArrayIndexOutOfBoundsException ABExc;
static ArithmeticException ArithExc;
static ClassCastException CCExc;
static IllegalMonitorStateException IMSExc;
static RetryException RetryExc;
//
// DON'T change order of first functions!!!
// interrupt gets called from jvm.asm
//
/**
* Dispatch an interrupt to the handler according to the core
* and the interrupt number. Interrupt 0 is the scheduler.
*/
static void interrupt() {
// the clean way
// ih[sys.cpuId][sys.intNr].run();
// a little bit faster
ih[Native.rd(Const.IO_CPU_ID)][Native.rd(Const.IO_INTNR)].run();
// Enable interrupts again - we could have invoked a dummy handler
Native.wr(1, Const.IO_INT_ENA);
// wr('!');
// wr('0'+nr);
}
public static void nullPoint() {
throw NPExc;
}
// this is used for arrays of longs
public static void arrayBound() {
throw ABExc;
}
public static void monitorState() {
throw IMSExc;
}
static int saved_sp;
/**
* Invoked on a hardware generated exception.
*/
static void except() {
saved_sp = Native.getSP();
if (Native.rdMem(Const.IO_EXCPT)==Const.EXC_SPOV) {
// reset stack pointer
Native.setSP(Const.STACK_OFF);
}
// we have more stack available now for the stack overflow
handleException();
}
static void noim() {
int i;
wr('n');
wr('i');
wr(' ');
i = Native.getSP(); // sp of noim();
int sp = Native.rdIntMem(i-4); // sp of calling function
int pc = Native.rdIntMem(sp-3)-1; // one to high
i = Native.rdIntMem(sp); // mp
wrSmall(i);
wr(' ');
int start = Native.rdMem(i)>>>10;
wrSmall(start);
wr(' ');
wrByte(pc);
wr(' ');
int val = Native.rdMem(start+(pc>>2));
for (i=(pc&0x03); i<3; ++i) val >>= 8;
val &= 0xff;
wrByte(val);
Object o = new Object();
synchronized (o) {
System.out.println();
System.out.print("JOP: bytecode ");
System.out.print(val);
System.out.println(" not implemented");
trace(sp);
for (;;);
}
}
static void handleException() {
if (Const.USE_RTTM) {
// abort transaction to avoid invoking f_athrow() twice
Native.wrMem(Const.TM_ABORTED, Const.MEM_TM_MAGIC);
}
int i;
i = Native.rdMem(Const.IO_EXCPT);
if (i==Const.EXC_SPOV) {
throw SOError;
} else if (i==Const.EXC_NP) {
throw NPExc;
} else if (i==Const.EXC_AB) {
throw ABExc;
} else if (i==Const.EXC_DIVZ) {
throw ArithExc;
} else if (i==Const.EXC_ROLLBACK) {
throw RetryExc;
} else if (i==Const.EXC_MON) {
throw IMSExc;
}
for (;;);
}
/**
* Create interrupt handler and preallocated exceptions
* in a static method instead of <clinit>.
* Jikes puts <clinit> as first methods into the table (instead of
* interrupt()), javac as last method.
*
* We could use HWO here.
*/
static void init() {
ih = new Runnable[Native.rdMem(Const.IO_CPUCNT)][Const.NUM_INTERRUPTS];
dh = new DummyHandler();
for (int core=0; core<Native.rdMem(Const.IO_CPUCNT); ++core) {
for (int var=0; var<Const.NUM_INTERRUPTS; ++var) {
JVMHelp.addInterruptHandler(core, var, dh);
}
}
sys = IOFactory.getFactory().getSysDevice();
SOError = new StackOverflowError();
NPExc = new NullPointerException();
ABExc = new ArrayIndexOutOfBoundsException();
ArithExc = new ArithmeticException();
CCExc = new ClassCastException();
IMSExc = new IllegalMonitorStateException();
RetryExc = RetryException.instance;
}
static void trace(int sp) {
int fp, mp, vp, pc, addr, loc, args;
int val;
// for (int i=0; i<1024; ++i) {
// wrSmall(i);
// wrSmall(Native.rdIntMem(i));
// wr('\n');
// }
wr("saved sp=");
wrSmall(sp);
wr('\n');
fp = sp-4; // first frame point is easy, since last sp points to the end of the frame
wr(" mp pc fp");
wr('\n');
while (fp>Const.STACK_OFF+5) {
mp = Native.rdIntMem(fp+4);
vp = Native.rdIntMem(fp+2);
pc = Native.rdIntMem(fp+1);
val = Native.rdMem(mp);
addr = val>>>10; // address of callee
wrSmall(mp);
// wrSmall(addr);
wrSmall(pc);
wrSmall(fp);
wr('\n');
val = Native.rdMem(mp+1); // cp, locals, args
args = val & 0x1f;
loc = (val>>>5) & 0x1f;
fp = vp+args+loc; // new fp can be calc. with vp and count of local vars
}
wr('\n');
}
/**
* Install a handle in two static fields for a hardware object
* @param o a 'real' instance of the HW object for the class reference
* @param address IO address of the hardware device
* @param idx index of the static fields
* @param cp address of constant pool of the factory class
* @return reference to the HW object
*/
public static Object makeHWObject(Object o, int address, int idx, int cp) {
int ref = Native.toInt(o);
int pcl = Native.rdMem(ref+1);
int p = Native.rdMem(cp-1);
p = Native.rdMem(p+1);
p += idx*2;
Native.wrMem(address, p);
Native.wrMem(pcl, p+1);
return Native.toObject(p);
}
public static int[] makeHWArray(int len, int address, int idx, int cp) {
int p = Native.rdMem(cp-1);
p = Native.rdMem(p+1);
p += idx*2;
Native.wrMem(address, p);
Native.wrMem(len, p+1);
return Native.toIntArray(p);
}
/**
* Add a Runnable as a first level interrupt handler.
* Use the current core.
* @param nr interrupt number
* @param r Runnable the represents the interrupt handler
*/
public static void addInterruptHandler(int nr, Runnable r) {
addInterruptHandler(Native.rdMem(Const.IO_CPU_ID), nr, r);
}
/**
* Add a Runnable as first level interrupt handler for an individual core.
* @param core
* @param nr
* @param r
*/
public static void addInterruptHandler(int core, int nr, Runnable r) {
if (nr>=0 && nr<ih[core].length) {
ih[core][nr] = r;
}
}
/**
* Remove the interrupt handler
* @param nr interrupt number
*/
public static void removeInterruptHandler(int nr) {
removeInterruptHandler(Native.rdMem(Const.IO_CPU_ID), nr);
}
/**
* Remove the interrupt handler
* @param nr interrupt number
*/
public static void removeInterruptHandler(int core, int nr) {
if (nr>=0 && nr<ih[core].length) {
ih[core][nr] = dh;
}
}
static void wrByte(int i) {
wr('0'+i/100);
wr('0'+i/10%10);
wr('0'+i%10);
wr(' ');
}
static void wrSmall(int i) {
wr('0'+i/100000%10);
wr('0'+i/10000%10);
wr('0'+i/1000%10);
wr('0'+i/100%10);
wr('0'+i/10%10);
wr('0'+i%10);
wr(' ');
}
public static void wr(int c) {
// busy wait on free tx buffer
// but ncts is not used anymore =>
// no wait on an open serial line, just wait
// on the baud rate
while ((Native.rd(Const.IO_STATUS)&1)==0) {
;
}
Native.wr(c, Const.IO_UART);
// this is the USB port
/* we will NOT wait for the USB device to be compatible
with other configurations. The UART limits the transfer rate
to about 10kB/s.
// while ((Native.rdMem(Const.IO_USB_STATUS) & Const.MSK_UA_TDRE)==0) {
;
}
*/
// disable for OEBB project
Native.wrMem(c, Const.IO_USB_DATA);
}
static void wr(String s) {
int i = s.length();
for (int j=0; j<i; ++j) {
wr(s.charAt(j));
}
}
private static final int MAX_TMP = 32;
private static int[] tmp; // a generic buffer
static void intVal(int val) {
// tmp is used before clazzinit runs
if (tmp==null) tmp = new int[MAX_TMP];
int i;
if (val<0) {
wr('-');
val = -val;
}
for (i=0; i<MAX_TMP-1; ++i) {
tmp[i] = (val%10)+'0';
val /= 10;
if (val==0) break;
}
for (val=i; val>=0; --val) {
wr(tmp[val]);
}
wr(' ');
}
// public static void scopeCheck(int ref, int val) {
//
// /** val has to be in a longer lived memory region than ref. This means that val can be in:
// *
// * 1. Immortal or Mission memory.
// * 2. A scope with a level smaller than the level of ref. (i.e. ref is in a deeper nested
// * scope than val).
// *
// */
//
// int ref_level;
// int val_level;
//
// if (Config.ADD_REF_INFO) {
// ref_level = (ref & 0x3E000000) >>> 25;
// val_level = (val & 0x3E000000) >>> 25;
//
// } else {
// ref_level = Native.rdMem(ref + GC.OFF_SCOPE_LEVEL);
// val_level = Native.rdMem(val + GC.OFF_SCOPE_LEVEL);
// }
//
// if (val_level == 0){
// if (ref_level != 0) { // ref is in scoped memory
// GC.log("Illegal Assignment Exception: Shorter lived object references longer lived object!");
// }
// } else { //val is in scope
// if (ref_level != 0){ // ref is in scope
// if (ref_level > val_level) { // ref is deeper nested than val
// GC.log("Illegal Assignment Exception: Scope level missmatch");
// }
// }
// }
// }
}
class DummyHandler implements Runnable {
public void run() {
// do nothing
}
}