Source code of MARS Assembler
First commit of the 4.5 version (latest version available)
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,460 @@
|
||||
package mars.simulator;
|
||||
import mars.*;
|
||||
import mars.venus.*;
|
||||
import mars.mips.hardware.*;
|
||||
import mars.mips.instructions.*;
|
||||
import java.util.*;
|
||||
|
||||
/*
|
||||
Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar
|
||||
|
||||
Developed by Pete Sanderson (psanderson@otterbein.edu)
|
||||
and Kenneth Vollmar (kenvollmar@missouristate.edu)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject
|
||||
to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
(MIT license, http://www.opensource.org/licenses/mit-license.html)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Used to "step backward" through execution, undoing each instruction.
|
||||
* @author Pete Sanderson
|
||||
* @version February 2006
|
||||
*/
|
||||
|
||||
public class BackStepper {
|
||||
// The types of "undo" actions. Under 1.5, these would be enumerated type.
|
||||
// These fit better in the BackStep class below but inner classes cannot have static members.
|
||||
private static final int MEMORY_RESTORE_RAW_WORD = 0;
|
||||
private static final int MEMORY_RESTORE_WORD = 1;
|
||||
private static final int MEMORY_RESTORE_HALF = 2;
|
||||
private static final int MEMORY_RESTORE_BYTE = 3;
|
||||
private static final int REGISTER_RESTORE = 4;
|
||||
private static final int PC_RESTORE = 5;
|
||||
private static final int COPROC0_REGISTER_RESTORE = 6;
|
||||
private static final int COPROC1_REGISTER_RESTORE = 7;
|
||||
private static final int COPROC1_CONDITION_CLEAR = 8;
|
||||
private static final int COPROC1_CONDITION_SET = 9;
|
||||
private static final int DO_NOTHING = 10; // instruction does not write anything.
|
||||
|
||||
// Flag to mark BackStep object as prepresenting specific situation: user manipulates
|
||||
// memory/register value via GUI after assembling program but before running it.
|
||||
private static final int NOT_PC_VALUE = -1;
|
||||
|
||||
private boolean engaged;
|
||||
private BackstepStack backSteps;
|
||||
|
||||
// One can argue using java.util.Stack, given its clumsy implementation.
|
||||
// A homegrown linked implementation will be more streamlined, but
|
||||
// I anticipate that backstepping will only be used during timed
|
||||
// (currently max 30 instructions/second) or stepped execution, where
|
||||
// performance is not an issue. Its Vector implementation may result
|
||||
// in quicker garbage collection than a pure linked list implementation.
|
||||
|
||||
/**
|
||||
* Create a fresh BackStepper. It is enabled, which means all
|
||||
* subsequent instruction executions will have their "undo" action
|
||||
* recorded here.
|
||||
*/
|
||||
public BackStepper() {
|
||||
engaged = true;
|
||||
backSteps = new BackstepStack(Globals.maximumBacksteps);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether execution "undo" steps are currently being recorded.
|
||||
* @return true if undo steps being recorded, false if not.
|
||||
*/
|
||||
public boolean enabled() {
|
||||
return engaged;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set enable status.
|
||||
* @param state If true, will begin (or continue) recoding "undo" steps. If false, will stop.
|
||||
*/
|
||||
public void setEnabled(boolean state) {
|
||||
engaged = state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether there are steps that can be undone.
|
||||
* @return true if there are no steps to be undone, false otherwise.
|
||||
*/
|
||||
public boolean empty() {
|
||||
return backSteps.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the next back-step action occurred as the result of
|
||||
* an instruction that executed in the "delay slot" of a delayed branch.
|
||||
* @return true if next backstep is instruction that executed in delay slot,
|
||||
* false otherwise.
|
||||
*/
|
||||
// Added 25 June 2007
|
||||
public boolean inDelaySlot() {
|
||||
return !empty() && backSteps.peek().inDelaySlot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Carry out a "back step", which will undo the latest execution step.
|
||||
* Does nothing if backstepping not enabled or if there are no steps to undo.
|
||||
*/
|
||||
|
||||
// Note that there may be more than one "step" in an instruction execution; for
|
||||
// instance the multiply, divide, and double-precision floating point operations
|
||||
// all store their result in register pairs which results in two store operations.
|
||||
// Both must be undone transparently, so we need to detect that multiple steps happen
|
||||
// together and carry out all of them here.
|
||||
// Use a do-while loop based on the backstep's program statement reference.
|
||||
|
||||
public void backStep() {
|
||||
if (engaged && !backSteps.empty()) {
|
||||
ProgramStatement statement = ((BackStep)backSteps.peek()).ps;
|
||||
engaged = false; // GOTTA DO THIS SO METHOD CALL IN SWITCH WILL NOT RESULT IN NEW ACTION ON STACK!
|
||||
do {
|
||||
BackStep step = (BackStep) backSteps.pop();
|
||||
/*
|
||||
System.out.println("backstep POP: action "+step.action+" pc "+mars.util.Binary.intToHexString(step.pc)+
|
||||
" source "+((step.ps==null)? "none":step.ps.getSource())+
|
||||
" parm1 "+step.param1+" parm2 "+step.param2);
|
||||
*/
|
||||
if (step.pc != NOT_PC_VALUE) {
|
||||
RegisterFile.setProgramCounter(step.pc);
|
||||
}
|
||||
try {
|
||||
switch (step.action) {
|
||||
case MEMORY_RESTORE_RAW_WORD :
|
||||
Globals.memory.setRawWord(step.param1, step.param2);
|
||||
break;
|
||||
case MEMORY_RESTORE_WORD :
|
||||
Globals.memory.setWord(step.param1, step.param2);
|
||||
break;
|
||||
case MEMORY_RESTORE_HALF :
|
||||
Globals.memory.setHalf(step.param1, step.param2);
|
||||
break;
|
||||
case MEMORY_RESTORE_BYTE :
|
||||
Globals.memory.setByte(step.param1, step.param2);
|
||||
break;
|
||||
case REGISTER_RESTORE :
|
||||
RegisterFile.updateRegister(step.param1, step.param2);
|
||||
break;
|
||||
case PC_RESTORE :
|
||||
RegisterFile.setProgramCounter(step.param1);
|
||||
break;
|
||||
case COPROC0_REGISTER_RESTORE :
|
||||
Coprocessor0.updateRegister(step.param1, step.param2);
|
||||
break;
|
||||
case COPROC1_REGISTER_RESTORE :
|
||||
Coprocessor1.updateRegister(step.param1, step.param2);
|
||||
break;
|
||||
case COPROC1_CONDITION_CLEAR :
|
||||
Coprocessor1.clearConditionFlag(step.param1);
|
||||
break;
|
||||
case COPROC1_CONDITION_SET :
|
||||
Coprocessor1.setConditionFlag(step.param1);
|
||||
break;
|
||||
case DO_NOTHING :
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
// if the original action did not cause an exception this will not either.
|
||||
System.out.println("Internal MARS error: address exception while back-stepping.");
|
||||
System.exit(0);
|
||||
}
|
||||
} while (!backSteps.empty() && statement == ((BackStep)backSteps.peek()).ps);
|
||||
engaged = true; // RESET IT (was disabled at top of loop -- see comment)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Convenience method called below to get program counter value. If it needs to be
|
||||
* be modified (e.g. to subtract 4) that can be done here in one place.
|
||||
*/
|
||||
|
||||
private int pc() {
|
||||
// PC incremented prior to instruction simulation, so need to adjust for that.
|
||||
return RegisterFile.getProgramCounter()-Instruction.INSTRUCTION_LENGTH;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new "back step" (the undo action) to the stack. The action here
|
||||
* is to restore a raw memory word value (setRawWord).
|
||||
* @param address The affected memory address.
|
||||
* @param value The "restore" value to be stored there.
|
||||
* @return the argument value
|
||||
*/
|
||||
public int addMemoryRestoreRawWord(int address, int value) {
|
||||
backSteps.push(MEMORY_RESTORE_RAW_WORD, pc(), address, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new "back step" (the undo action) to the stack. The action here
|
||||
* is to restore a memory word value.
|
||||
* @param address The affected memory address.
|
||||
* @param value The "restore" value to be stored there.
|
||||
* @return the argument value
|
||||
*/
|
||||
public int addMemoryRestoreWord(int address, int value) {
|
||||
backSteps.push(MEMORY_RESTORE_WORD, pc(), address, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new "back step" (the undo action) to the stack. The action here
|
||||
* is to restore a memory half-word value.
|
||||
* @param address The affected memory address.
|
||||
* @param value The "restore" value to be stored there, in low order half.
|
||||
* @return the argument value
|
||||
*/
|
||||
public int addMemoryRestoreHalf(int address, int value) {
|
||||
backSteps.push(MEMORY_RESTORE_HALF, pc(), address, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new "back step" (the undo action) to the stack. The action here
|
||||
* is to restore a memory byte value.
|
||||
* @param address The affected memory address.
|
||||
* @param value The "restore" value to be stored there, in low order byte.
|
||||
* @return the argument value
|
||||
*/
|
||||
public int addMemoryRestoreByte(int address, int value) {
|
||||
backSteps.push(MEMORY_RESTORE_BYTE, pc(), address, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new "back step" (the undo action) to the stack. The action here
|
||||
* is to restore a register file register value.
|
||||
* @param register The affected register number.
|
||||
* @param value The "restore" value to be stored there.
|
||||
* @return the argument value
|
||||
*/
|
||||
public int addRegisterFileRestore(int register, int value) {
|
||||
backSteps.push(REGISTER_RESTORE, pc(), register, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new "back step" (the undo action) to the stack. The action here
|
||||
* is to restore the program counter.
|
||||
* @param value The "restore" value to be stored there.
|
||||
* @return the argument value
|
||||
*/
|
||||
public int addPCRestore(int value) {
|
||||
// adjust for value reflecting incremented PC.
|
||||
value -= Instruction.INSTRUCTION_LENGTH;
|
||||
// Use "value" insead of "pc()" for second arg because RegisterFile.getProgramCounter()
|
||||
// returns branch target address at this point.
|
||||
backSteps.push(PC_RESTORE, value, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new "back step" (the undo action) to the stack. The action here
|
||||
* is to restore a coprocessor 0 register value.
|
||||
* @param register The affected register number.
|
||||
* @param value The "restore" value to be stored there.
|
||||
* @return the argument value
|
||||
*/
|
||||
public int addCoprocessor0Restore(int register, int value) {
|
||||
backSteps.push(COPROC0_REGISTER_RESTORE, pc(), register, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new "back step" (the undo action) to the stack. The action here
|
||||
* is to restore a coprocessor 1 register value.
|
||||
* @param register The affected register number.
|
||||
* @param value The "restore" value to be stored there.
|
||||
* @return the argument value
|
||||
*/
|
||||
public int addCoprocessor1Restore(int register, int value) {
|
||||
backSteps.push(COPROC1_REGISTER_RESTORE, pc(), register, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new "back step" (the undo action) to the stack. The action here
|
||||
* is to set the given coprocessor 1 condition flag (to 1).
|
||||
* @param flag The condition flag number.
|
||||
* @return the argument value
|
||||
*/
|
||||
public int addConditionFlagSet(int flag) {
|
||||
backSteps.push(COPROC1_CONDITION_SET, pc(), flag);
|
||||
return flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new "back step" (the undo action) to the stack. The action here
|
||||
* is to clear the given coprocessor 1 condition flag (to 0).
|
||||
* @param flag The condition flag number.
|
||||
* @return the argument value
|
||||
*/
|
||||
public int addConditionFlagClear(int flag) {
|
||||
backSteps.push(COPROC1_CONDITION_CLEAR, pc(), flag);
|
||||
return flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new "back step" (the undo action) to the stack. The action here
|
||||
* is to do nothing! This is just a place holder so when user is backstepping
|
||||
* through the program no instructions will be skipped. Cosmetic. If the top of the
|
||||
* stack has the same PC counter, the do-nothing action will not be added.
|
||||
* @return 0
|
||||
*/
|
||||
public int addDoNothing(int pc) {
|
||||
if (backSteps.empty() || backSteps.peek().pc != pc) {
|
||||
backSteps.push(DO_NOTHING, pc);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Represents a "back step" (undo action) on the stack.
|
||||
private class BackStep {
|
||||
private int action; // what do do MEMORY_RESTORE_WORD, etc
|
||||
private int pc; // program counter value when original step occurred
|
||||
private ProgramStatement ps; // statement whose action is being "undone" here
|
||||
private int param1; // first parameter required by that action
|
||||
private int param2; // optional second parameter required by that action
|
||||
private boolean inDelaySlot; // true if instruction executed in "delay slot" (delayed branching enabled)
|
||||
|
||||
// it is critical that BackStep object get its values by calling this method
|
||||
// rather than assigning to individual members, because of the technique used
|
||||
// to set its ps member (and possibly pc).
|
||||
private void assign(int act, int programCounter, int parm1, int parm2) {
|
||||
action = act;
|
||||
pc = programCounter;
|
||||
try {
|
||||
// Client does not have direct access to program statement, and rather than making all
|
||||
// of them go through the methods below to obtain it, we will do it here.
|
||||
// Want the program statement but do not want observers notified.
|
||||
ps = Globals.memory.getStatementNoNotify(programCounter);
|
||||
}
|
||||
catch (Exception e) {
|
||||
// The only situation causing this so far: user modifies memory or register
|
||||
// contents through direct manipulation on the GUI, after assembling the program but
|
||||
// before starting to run it (or after backstepping all the way to the start).
|
||||
// The action will not be associated with any instruction, but will be carried out
|
||||
// when popped.
|
||||
ps = null;
|
||||
pc = NOT_PC_VALUE; // Backstep method above will see this as flag to not set PC
|
||||
}
|
||||
param1 = parm1;
|
||||
param2 = parm2;
|
||||
inDelaySlot = Simulator.inDelaySlot(); // ADDED 25 June 2007
|
||||
/*
|
||||
System.out.println("backstep PUSH: action "+action+" pc "+mars.util.Binary.intToHexString(pc)+
|
||||
" source "+((ps==null)? "none":ps.getSource())+
|
||||
" parm1 "+param1+" parm2 "+param2);
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
// *****************************************************************************
|
||||
// special purpose stack class for backstepping. You've heard of circular queues
|
||||
// implemented with an array, right? This is a circular stack! When full, the
|
||||
// newly-pushed item overwrites the oldest item, with circular top! All operations
|
||||
// are constant time. It's synchronized too, to be safe (is used by both the
|
||||
// simulation thread and the GUI thread for the back-step button).
|
||||
// Upon construction, it is filled with newly-created empty BackStep objects which
|
||||
// will exist for the life of the stack. Push does not create a BackStep object
|
||||
// but instead overwrites the contents of the existing one. Thus during MIPS
|
||||
// program (simulated) execution, BackStep objects are never created or junked
|
||||
// regardless of how many steps are executed. This will speed things up a bit
|
||||
// and make life easier for the garbage collector.
|
||||
|
||||
private class BackstepStack {
|
||||
private int capacity;
|
||||
private int size;
|
||||
private int top;
|
||||
private BackStep[] stack;
|
||||
|
||||
// Stack is created upon successful assembly or reset. The one-time overhead of
|
||||
// creating all the BackStep objects will not be noticed by the user, and enhances
|
||||
// runtime performance by not having to create or recycle them during MIPS
|
||||
// program execution.
|
||||
private BackstepStack(int capacity) {
|
||||
this.capacity = capacity;
|
||||
this.size = 0;
|
||||
this.top = -1;
|
||||
this.stack = new BackStep[capacity];
|
||||
for (int i=0; i<capacity; i++) {
|
||||
this.stack[i] = new BackStep();
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized boolean empty() {
|
||||
return size==0;
|
||||
}
|
||||
|
||||
private synchronized void push(int act, int programCounter, int parm1, int parm2) {
|
||||
if (size==0) {
|
||||
top=0;
|
||||
size++;
|
||||
}
|
||||
else if (size < capacity) {
|
||||
top = (top + 1) % capacity;
|
||||
size++;
|
||||
}
|
||||
else { // size == capacity. The top moves up one, replacing oldest entry (goodbye!)
|
||||
top = (top + 1) % capacity;
|
||||
}
|
||||
// We'll re-use existing objects rather than create/discard each time.
|
||||
// Must use assign() method rather than series of assignment statements!
|
||||
stack[top].assign(act, programCounter, parm1, parm2);
|
||||
}
|
||||
|
||||
private synchronized void push(int act, int programCounter, int parm1) {
|
||||
push(act, programCounter, parm1, 0);
|
||||
}
|
||||
|
||||
private synchronized void push(int act, int programCounter) {
|
||||
push(act, programCounter, 0, 0);
|
||||
}
|
||||
|
||||
// NO PROTECTION. This class is used only within this file so there is no excuse
|
||||
// for trying to pop from empty stack.
|
||||
private synchronized BackStep pop() {
|
||||
BackStep bs;
|
||||
bs = stack[top];
|
||||
if (size==1) {
|
||||
top = -1;
|
||||
}
|
||||
else {
|
||||
top = (top + capacity - 1) % capacity;
|
||||
}
|
||||
size--;
|
||||
return bs;
|
||||
}
|
||||
|
||||
// NO PROTECTION. This class is used only within this file so there is no excuse
|
||||
// for trying to peek from empty stack.
|
||||
private synchronized BackStep peek() {
|
||||
return stack[top];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,159 @@
|
||||
package mars.simulator;
|
||||
|
||||
/*
|
||||
Copyright (c) 2003-2007, Pete Sanderson and Kenneth Vollmar
|
||||
|
||||
Developed by Pete Sanderson (psanderson@otterbein.edu)
|
||||
and Kenneth Vollmar (kenvollmar@missouristate.edu)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject
|
||||
to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
(MIT license, http://www.opensource.org/licenses/mit-license.html)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a (potential) delayed branch. Note it is necessary only when
|
||||
* delayed branching is enabled. Here's the protocol for using it:
|
||||
*
|
||||
* (1) When a runtime decision to branch is made (by either a branch or jump
|
||||
* instruction's simulate() method in InstructionSet), then if delayed branching
|
||||
* is enabled, the register() method is called with the branch target address but
|
||||
* the program counter is NOT set to the branch target address.
|
||||
*
|
||||
* (2) At the end of that instruction cycle, the simulate() method in Simulator
|
||||
* will detect the registered branch, and set its trigger. Don't do anything yet
|
||||
* because the next instruction cycle is the delay slot and needs to complete.
|
||||
*
|
||||
* (3) At the end of the next (delay slot) instruction cycle, the simulate()
|
||||
* method in Simulator will detect the triggered branch, set the program
|
||||
* counter to its target value and clear the delayed branch.
|
||||
*
|
||||
* The only interesting situation is when the delay slot itself contains a
|
||||
* successful branch! I tried this with SPIM (e.g. beq followed by b)
|
||||
* and it treats it as if nothing was there and continues the delay slot
|
||||
* into the next cycle. The eventual branch taken is the original one (as one
|
||||
* would hope) but in the meantime the first statement following the sequence
|
||||
* of successful branches will constitute the delay slot and will be executed!
|
||||
*
|
||||
* Since only one pending delayed branch can be taken at a time, everything
|
||||
* here is done with statics. The class itself represents the potential branch.
|
||||
*
|
||||
* @author Pete Sanderson
|
||||
* @version June 2007
|
||||
**/
|
||||
|
||||
public class DelayedBranch {
|
||||
// Class states.
|
||||
private static final int CLEARED = 0;
|
||||
private static final int REGISTERED = 1;
|
||||
private static final int TRIGGERED = 2;
|
||||
|
||||
// Initially nothing is happening.
|
||||
|
||||
private static int state = CLEARED;
|
||||
private static int branchTargetAddress = 0;
|
||||
|
||||
/**
|
||||
* Register the fact that a successful branch is to occur. This is called in
|
||||
* the instruction's simulated execution (its simulate() method in InstructionSet).
|
||||
* If a branch is registered but not triggered, this registration will be ignored
|
||||
* (cannot happen if class usage protocol is followed). If a branch is currently
|
||||
* registered and triggered, reset the state back to registered (but not triggered)
|
||||
* in order to carry over the delay slot for another execution cycle. This is the
|
||||
* only public member of the class.
|
||||
*
|
||||
* @param targetAddress The address to branch to after executing the next instruction
|
||||
*/
|
||||
public static void register(int targetAddress) {
|
||||
// About as clean as a switch statement can be!
|
||||
switch (state) {
|
||||
case CLEARED : branchTargetAddress = targetAddress;
|
||||
case REGISTERED :
|
||||
case TRIGGERED : state = REGISTERED;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger a registered branch. This is called at the end of the MIPS simulator
|
||||
* instruction execution cycle (simulate method in Simulator), so a registered
|
||||
* branch will be triggered right away. The next execution cycle will be the
|
||||
* delay slot and at the end of THAT cycle, the trigger will be detected and the
|
||||
* branch carried out. This method has package visibility.
|
||||
*
|
||||
* Precondition: DelayedBranch.isRegistered()
|
||||
*
|
||||
* Postcondition: DelayedBranch.isTriggered() && !DelayedBranch.isRegistered()
|
||||
*
|
||||
*/
|
||||
static void trigger() {
|
||||
// About as clean as a switch statement can be!
|
||||
switch (state) {
|
||||
case REGISTERED :
|
||||
case TRIGGERED : state = TRIGGERED;
|
||||
case CLEARED :
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the delayed branch. This must be done immediately after setting the
|
||||
* program counter to the target address. This method has package visibility.
|
||||
*/
|
||||
static void clear() {
|
||||
state = CLEARED;
|
||||
branchTargetAddress = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return registration status. Is false initially, true after register() is called
|
||||
* but becomes false after trigger() or clear() are called. This method has package
|
||||
* visibility.
|
||||
*
|
||||
* @return true if branch is registered but not triggered, false otherwise.
|
||||
*/
|
||||
|
||||
static boolean isRegistered() {
|
||||
return state == REGISTERED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return trigger status. Is false initially, true after trigger() is called
|
||||
* but becomes false after clear() is called. This method has package visibility.
|
||||
*
|
||||
* @return true if branch is registered but not triggered, false otherwise.
|
||||
*/
|
||||
|
||||
static boolean isTriggered() {
|
||||
return state == TRIGGERED;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return branch target address. This should be retrieved only to set the program
|
||||
* counter at the end of the delay slot. This method has package visibility.
|
||||
*
|
||||
* Precondition: DelayedBranch.isTriggered()
|
||||
*
|
||||
* @return Target address of the delayed branch.
|
||||
*/
|
||||
static int getBranchTargetAddress() {
|
||||
return branchTargetAddress;
|
||||
}
|
||||
|
||||
} // DelayedBranch
|
||||
Binary file not shown.
@@ -0,0 +1,97 @@
|
||||
package mars.simulator;
|
||||
import mars.mips.hardware.*;
|
||||
import mars.mips.instructions.*;
|
||||
import mars.util.*;
|
||||
|
||||
/*
|
||||
Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar
|
||||
|
||||
Developed by Pete Sanderson (psanderson@otterbein.edu)
|
||||
and Kenneth Vollmar (kenvollmar@missouristate.edu)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject
|
||||
to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
(MIT license, http://www.opensource.org/licenses/mit-license.html)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents an error/interrupt that occurs during execution (simulation).
|
||||
* @author Pete Sanderson
|
||||
* @version August 2005
|
||||
**/
|
||||
|
||||
public class Exceptions {
|
||||
/** The exception number is stored in coprocessor 0 cause register ($13)
|
||||
* Note: the codes for External Interrupts have been modified from MIPS
|
||||
* specs in order to encode two pieces of information. According
|
||||
* to spec, there is one External Interrupt code, 0. But then
|
||||
* how to distinguish keyboard interrupt from display interrupt?
|
||||
* The Cause register has Interupt Pending bits that can be set.
|
||||
* Bit 8 represents keyboard, bit 9 represents display. Those
|
||||
* bits are included into this code, but shifted right two positions
|
||||
* since the interrupt code will be shifted left two positions
|
||||
* for inserting cause code into bit positions 2-6 in Cause register.
|
||||
* DPS 23 July 2008.
|
||||
*/
|
||||
public static final int EXTERNAL_INTERRUPT_KEYBOARD = 0x00000040; // see comment above.
|
||||
public static final int EXTERNAL_INTERRUPT_DISPLAY = 0x00000080; // see comment above.
|
||||
public static final int ADDRESS_EXCEPTION_LOAD = 4;
|
||||
public static final int ADDRESS_EXCEPTION_STORE = 5;
|
||||
public static final int SYSCALL_EXCEPTION = 8;
|
||||
public static final int BREAKPOINT_EXCEPTION = 9;
|
||||
public static final int RESERVED_INSTRUCTION_EXCEPTION = 10;
|
||||
public static final int ARITHMETIC_OVERFLOW_EXCEPTION = 12;
|
||||
public static final int TRAP_EXCEPTION = 13;
|
||||
/* the following are from SPIM */
|
||||
public static final int DIVIDE_BY_ZERO_EXCEPTION = 15;
|
||||
public static final int FLOATING_POINT_OVERFLOW = 16;
|
||||
public static final int FLOATING_POINT_UNDERFLOW = 17;
|
||||
|
||||
/**
|
||||
* Given MIPS exception cause code, will place that code into
|
||||
* coprocessor 0 CAUSE register ($13), set the EPC register to
|
||||
* "current" program counter, and set Exception Level bit in STATUS register.
|
||||
*
|
||||
* @param cause The cause code (see Exceptions for a list)
|
||||
*/
|
||||
public static void setRegisters(int cause) {
|
||||
// Set CAUSE register bits 2 thru 6 to cause value. The "& 0xFFFFFC83" will set bits 2-6 and 8-9 to 0 while
|
||||
// keeping all the others. Left-shift by 2 to put cause value into position then OR it in. Bits 8-9 used to
|
||||
// identify devices for External Interrupt (8=keyboard,9=display).
|
||||
Coprocessor0.updateRegister(Coprocessor0.CAUSE,(Coprocessor0.getValue(Coprocessor0.CAUSE) & 0xFFFFFC83 | (cause << 2)));
|
||||
// When exception occurred, PC had already been incremented so need to subtract 4 here.
|
||||
Coprocessor0.updateRegister(Coprocessor0.EPC, RegisterFile.getProgramCounter()-Instruction.INSTRUCTION_LENGTH);
|
||||
// Set EXL (Exception Level) bit, bit position 1, in STATUS register to 1.
|
||||
Coprocessor0.updateRegister(Coprocessor0.STATUS, Binary.setBit(Coprocessor0.getValue(Coprocessor0.STATUS), Coprocessor0.EXCEPTION_LEVEL));
|
||||
}
|
||||
|
||||
/**
|
||||
* Given MIPS exception cause code and bad address, place the bad address into VADDR
|
||||
* register ($8) then call overloaded setRegisters with the cause code to do the rest.
|
||||
*
|
||||
* @param cause The cause code (see Exceptions for a list). Should be address exception.
|
||||
* @param addr The address that caused the exception.
|
||||
*/
|
||||
public static void setRegisters(int cause, int addr) {
|
||||
Coprocessor0.updateRegister(Coprocessor0.VADDR,addr);
|
||||
setRegisters(cause);
|
||||
}
|
||||
|
||||
} // Exceptions
|
||||
Binary file not shown.
@@ -0,0 +1,207 @@
|
||||
package mars.simulator;
|
||||
import mars.*;
|
||||
import mars.venus.*;
|
||||
import mars.util.*;
|
||||
import mars.mips.hardware.*;
|
||||
import mars.mips.instructions.*;
|
||||
import java.util.*;
|
||||
import javax.swing.*;
|
||||
import java.awt.event.*;
|
||||
|
||||
/*
|
||||
Copyright (c) 2003-2008, Pete Sanderson and Kenneth Vollmar
|
||||
|
||||
Developed by Pete Sanderson (psanderson@otterbein.edu)
|
||||
and Kenneth Vollmar (kenvollmar@missouristate.edu)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject
|
||||
to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
(MIT license, http://www.opensource.org/licenses/mit-license.html)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Models Program Arguments, one or more strings provided to the MIPS
|
||||
* program at runtime. Equivalent to C's main(int argc, char **argv) or
|
||||
* Java's main(String[] args).
|
||||
* @author Pete Sanderson
|
||||
* @version July 2008
|
||||
**/
|
||||
|
||||
public class ProgramArgumentList {
|
||||
|
||||
ArrayList programArgumentList;
|
||||
|
||||
/**
|
||||
* Constructor that parses string to produce list. Delimiters
|
||||
* are the default Java StringTokenizer delimiters (space, tab,
|
||||
* newline, return, formfeed)
|
||||
*
|
||||
* @param args String containing delimiter-separated arguments
|
||||
*/
|
||||
public ProgramArgumentList(String args) {
|
||||
StringTokenizer st = new StringTokenizer(args);
|
||||
programArgumentList = new ArrayList(st.countTokens());
|
||||
while (st.hasMoreTokens()) {
|
||||
programArgumentList.add(st.nextToken());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor that gets list from String array, one argument per element.
|
||||
*
|
||||
* @param list Array of String, each element containing one argument
|
||||
*/
|
||||
public ProgramArgumentList(String[] list) {
|
||||
this(list, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor that gets list from section of String array, one
|
||||
* argument per element.
|
||||
*
|
||||
* @param args Array of String, each element containing one argument
|
||||
* @param startPosition Index of array element containing the first argument; all remaining
|
||||
* elements are assumed to contain an argument.
|
||||
*/
|
||||
public ProgramArgumentList(String[] list, int startPosition) {
|
||||
programArgumentList = new ArrayList(list.length-startPosition);
|
||||
for (int i=startPosition; i<list.length; i++) {
|
||||
programArgumentList.add(list[i]);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Constructor that gets list from ArrayList of String, one argument per element.
|
||||
*
|
||||
* @param list ArrayList of String, each element containing one argument
|
||||
*/
|
||||
public ProgramArgumentList(ArrayList list) {
|
||||
this(list, 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructor that gets list from section of String ArrayList, one
|
||||
* argument per element.
|
||||
*
|
||||
* @param args ArrayList of String, each element containing one argument
|
||||
* @param startPosition Index of array element containing the first argument; all remaining
|
||||
* elements are assumed to contain an argument.
|
||||
*/
|
||||
public ProgramArgumentList(ArrayList list, int startPosition) {
|
||||
if (list == null || list.size() < startPosition) {
|
||||
programArgumentList = new ArrayList(0);
|
||||
}
|
||||
else {
|
||||
programArgumentList = new ArrayList(list.size()-startPosition);
|
||||
for (int i=startPosition; i<list.size(); i++) {
|
||||
programArgumentList.add(list.get(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Place any program arguments into MIPS memory and registers
|
||||
// Arguments are stored starting at highest word of non-kernel
|
||||
// memory and working back toward runtime stack (there is a 4096
|
||||
// byte gap in between). The argument count (argc) and pointers
|
||||
// to the arguments are stored on the runtime stack. The stack
|
||||
// pointer register $sp is adjusted accordingly and $a0 is set
|
||||
// to the argument count (argc), and $a1 is set to the stack
|
||||
// address holding the first argument pointer (argv).
|
||||
public void storeProgramArguments() {
|
||||
if (programArgumentList == null || programArgumentList.size() == 0) {
|
||||
return;
|
||||
}
|
||||
// Runtime stack initialization from stack top-down (each is 4 bytes) :
|
||||
// programArgumentList.size()
|
||||
// address of first character of first program argument
|
||||
// address of first character of second program argument
|
||||
// ....repeat for all program arguments
|
||||
// 0x00000000 (null terminator for list of string pointers)
|
||||
// $sp will be set to the address holding the arg list size
|
||||
// $a0 will be set to the arg list size (argc)
|
||||
// $a1 will be set to stack address just "below" arg list size (argv)
|
||||
//
|
||||
// Each of the arguments themselves will be stored starting at
|
||||
// Memory.stackBaseAddress (0x7ffffffc) and working down from there:
|
||||
// 0x7ffffffc will contain null terminator for first arg
|
||||
// 0x7ffffffb will contain last character of first arg
|
||||
// 0x7ffffffa will contain next-to-last character of first arg
|
||||
// Etc down to first character of first arg.
|
||||
// Previous address will contain null terminator for second arg
|
||||
// Previous-to-that contains last character of second arg
|
||||
// Etc down to first character of second arg.
|
||||
// Follow this pattern for all remaining arguments.
|
||||
|
||||
|
||||
int highAddress = Memory.stackBaseAddress; // highest non-kernel address, sits "under" stack
|
||||
String programArgument;
|
||||
int[] argStartAddress = new int[programArgumentList.size()];
|
||||
try { // needed for all memory writes
|
||||
for (int i=0; i<programArgumentList.size(); i++) {
|
||||
programArgument = (String) programArgumentList.get(i);
|
||||
Globals.memory.set(highAddress, 0, 1); // trailing null byte for each argument
|
||||
highAddress--;
|
||||
for (int j = programArgument.length()-1; j >= 0; j--) {
|
||||
Globals.memory.set(highAddress, programArgument.charAt(j), 1);
|
||||
highAddress--;
|
||||
}
|
||||
argStartAddress[i] = highAddress+1;
|
||||
}
|
||||
// now place a null word, the arg starting addresses, and arg count onto stack.
|
||||
int stackAddress = Memory.stackPointer; // base address for runtime stack.
|
||||
if (highAddress < Memory.stackPointer) {
|
||||
// Based on current values for stackBaseAddress and stackPointer, this will
|
||||
// only happen if the combined lengths of program arguments is greater than
|
||||
// 0x7ffffffc - 0x7fffeffc = 0x00001000 = 4096 bytes. In this case, set
|
||||
// stackAddress to next lower word boundary minus 4 for clearance (since every
|
||||
// byte from highAddress+1 is filled).
|
||||
stackAddress = highAddress - (highAddress % Memory.WORD_LENGTH_BYTES) - Memory.WORD_LENGTH_BYTES;
|
||||
}
|
||||
Globals.memory.set(stackAddress, 0, Memory.WORD_LENGTH_BYTES); // null word for end of argv array
|
||||
stackAddress -= Memory.WORD_LENGTH_BYTES;
|
||||
for (int i=argStartAddress.length-1; i >= 0; i--) {
|
||||
Globals.memory.set(stackAddress, argStartAddress[i], Memory.WORD_LENGTH_BYTES);
|
||||
stackAddress -= Memory.WORD_LENGTH_BYTES;
|
||||
}
|
||||
Globals.memory.set(stackAddress, argStartAddress.length, Memory.WORD_LENGTH_BYTES); // argc
|
||||
stackAddress -= Memory.WORD_LENGTH_BYTES;
|
||||
|
||||
// Need to set $sp register to stack address, $a0 to argc, $a1 to argv
|
||||
// Need to by-pass the backstepping mechanism so go directly to Register instead of RegisterFile
|
||||
Register[] registers = RegisterFile.getRegisters();
|
||||
RegisterFile.getUserRegister("$sp").setValue(stackAddress+Memory.WORD_LENGTH_BYTES);
|
||||
RegisterFile.getUserRegister("$a0").setValue(argStartAddress.length); // argc
|
||||
RegisterFile.getUserRegister("$a1").setValue(stackAddress+Memory.WORD_LENGTH_BYTES+Memory.WORD_LENGTH_BYTES); // argv
|
||||
//RegisterFile.updateRegister("$sp",stackAddress+Memory.WORD_LENGTH_BYTES);
|
||||
//RegisterFile.updateRegister("$a0",argStartAddress.length); // argc
|
||||
//RegisterFile.updateRegister("$a1",stackAddress+Memory.WORD_LENGTH_BYTES+Memory.WORD_LENGTH_BYTES); // argv
|
||||
}
|
||||
catch (AddressErrorException aee) {
|
||||
System.out.println("Internal Error: Memory write error occurred while storing program arguments! "+aee);
|
||||
System.exit(0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,536 @@
|
||||
package mars.simulator;
|
||||
import mars.*;
|
||||
import mars.venus.*;
|
||||
import mars.util.*;
|
||||
import mars.mips.hardware.*;
|
||||
import mars.mips.instructions.*;
|
||||
import java.util.*;
|
||||
import javax.swing.*;
|
||||
import java.awt.event.*;
|
||||
|
||||
/*
|
||||
Copyright (c) 2003-2010, Pete Sanderson and Kenneth Vollmar
|
||||
|
||||
Developed by Pete Sanderson (psanderson@otterbein.edu)
|
||||
and Kenneth Vollmar (kenvollmar@missouristate.edu)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject
|
||||
to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
(MIT license, http://www.opensource.org/licenses/mit-license.html)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Used to simulate the execution of an assembled MIPS program.
|
||||
* @author Pete Sanderson
|
||||
* @version August 2005
|
||||
**/
|
||||
|
||||
public class Simulator extends Observable {
|
||||
private SimThread simulatorThread;
|
||||
private static Simulator simulator = null; // Singleton object
|
||||
private static Runnable interactiveGUIUpdater = null;
|
||||
// Others can set this true to indicate external interrupt. Initially used
|
||||
// to simulate keyboard and display interrupts. The device is identified
|
||||
// by the address of its MMIO control register. keyboard 0xFFFF0000 and
|
||||
// display 0xFFFF0008. DPS 23 July 2008.
|
||||
public static final int NO_DEVICE = 0;
|
||||
public static volatile int externalInterruptingDevice = NO_DEVICE;
|
||||
/** various reasons for simulate to end... */
|
||||
public static final int BREAKPOINT = 1;
|
||||
public static final int EXCEPTION = 2;
|
||||
public static final int MAX_STEPS = 3; // includes step mode (where maxSteps is 1)
|
||||
public static final int NORMAL_TERMINATION = 4;
|
||||
public static final int CLIFF_TERMINATION = 5; // run off bottom of program
|
||||
public static final int PAUSE_OR_STOP = 6;
|
||||
|
||||
/**
|
||||
* Returns the Simulator object
|
||||
*
|
||||
* @return the Simulator object in use
|
||||
*/
|
||||
public static Simulator getInstance() {
|
||||
// Do NOT change this to create the Simulator at load time (in declaration above)!
|
||||
// Its constructor looks for the GUI, which at load time is not created yet,
|
||||
// and incorrectly leaves interactiveGUIUpdater null! This causes runtime
|
||||
// exceptions while running in timed mode.
|
||||
if (simulator==null) {
|
||||
simulator = new Simulator();
|
||||
}
|
||||
return simulator;
|
||||
}
|
||||
|
||||
private Simulator() {
|
||||
simulatorThread = null;
|
||||
if (Globals.getGui() != null) {
|
||||
interactiveGUIUpdater = new UpdateGUI();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Determine whether or not the next instruction to be executed is in a
|
||||
* "delay slot". This means delayed branching is enabled, the branch
|
||||
* condition has evaluated true, and the next instruction executed will
|
||||
* be the one following the branch. It is said to occupy the "delay slot."
|
||||
* Normally programmers put a nop instruction here but it can be anything.
|
||||
*
|
||||
* @return true if next instruction is in delay slot, false otherwise.
|
||||
*/
|
||||
|
||||
public static boolean inDelaySlot() {
|
||||
return DelayedBranch.isTriggered();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Simulate execution of given MIPS program. It must have already been assembled.
|
||||
* @param p The MIPSprogram to be simulated.
|
||||
* @param pc address of first instruction to simulate; this goes into program counter
|
||||
* @param maxSteps maximum number of steps to perform before returning false (0 or less means no max)
|
||||
* @param breakPoints array of breakpoint program counter values, use null if none
|
||||
* @param actor the GUI component responsible for this call, usually GO or STEP. null if none.
|
||||
* @return true if execution completed, false otherwise
|
||||
* @throws ProcessingException Throws exception if run-time exception occurs.
|
||||
**/
|
||||
|
||||
public boolean simulate(MIPSprogram p, int pc, int maxSteps, int[] breakPoints, AbstractAction actor) throws ProcessingException {
|
||||
simulatorThread = new SimThread(p,pc,maxSteps,breakPoints,actor);
|
||||
simulatorThread.start();
|
||||
|
||||
// Condition should only be true if run from command-line instead of GUI.
|
||||
// If so, just stick around until execution thread is finished.
|
||||
if (actor == null) {
|
||||
Object dun = simulatorThread.get(); // this should emulate join()
|
||||
ProcessingException pe = simulatorThread.pe;
|
||||
boolean done = simulatorThread.done;
|
||||
if (done) SystemIO.resetFiles(); // close any files opened in MIPS progra
|
||||
this.simulatorThread = null;
|
||||
if (pe != null) {
|
||||
throw pe;
|
||||
}
|
||||
return done;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the volatile stop boolean variable checked by the execution
|
||||
* thread at the end of each MIPS instruction execution. If variable
|
||||
* is found to be true, the execution thread will depart
|
||||
* gracefully so the main thread handling the GUI can take over.
|
||||
* This is used by both STOP and PAUSE features.
|
||||
*/
|
||||
public void stopExecution(AbstractAction actor) {
|
||||
|
||||
if (simulatorThread != null) {
|
||||
simulatorThread.setStop(actor);
|
||||
for (StopListener l : stopListeners) {
|
||||
l.stopped(this);
|
||||
}
|
||||
simulatorThread = null;
|
||||
}
|
||||
}
|
||||
|
||||
/* This interface is required by the Asker class in MassagesPane
|
||||
* to be notified about the fact that the user has requested to
|
||||
* stop the execution. When that happens, it must unblock the
|
||||
* simulator thread. */
|
||||
public interface StopListener {
|
||||
void stopped(Simulator s);
|
||||
}
|
||||
|
||||
private ArrayList<StopListener> stopListeners = new ArrayList<StopListener>(1);
|
||||
public void addStopListener(StopListener l) {
|
||||
stopListeners.add(l);
|
||||
}
|
||||
|
||||
public void removeStopListener(StopListener l) {
|
||||
stopListeners.remove(l);
|
||||
}
|
||||
|
||||
// The Simthread object will call this method when it enters and returns from
|
||||
// its construct() method. These signal start and stop, respectively, of
|
||||
// simulation execution. The observer can then adjust its own state depending
|
||||
// on the execution state. Note that "stop" and "done" are not the same thing.
|
||||
// "stop" just means it is leaving execution state; this could be triggered
|
||||
// by Stop button, by Pause button, by Step button, by runtime exception, by
|
||||
// instruction count limit, by breakpoint, or by end of simulation (truly done).
|
||||
private void notifyObserversOfExecutionStart(int maxSteps, int programCounter) {
|
||||
this.setChanged();
|
||||
this.notifyObservers(new SimulatorNotice(SimulatorNotice.SIMULATOR_START,
|
||||
maxSteps, RunSpeedPanel.getInstance().getRunSpeed(), programCounter) );
|
||||
}
|
||||
|
||||
private void notifyObserversOfExecutionStop(int maxSteps, int programCounter) {
|
||||
this.setChanged();
|
||||
this.notifyObservers(new SimulatorNotice(SimulatorNotice.SIMULATOR_STOP,
|
||||
maxSteps, RunSpeedPanel.getInstance().getRunSpeed(), programCounter) );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* SwingWorker subclass to perform the simulated execution in background thread.
|
||||
* It is "interrupted" when main thread sets the "stop" variable to true.
|
||||
* The variable is tested before the next MIPS instruction is simulated. Thus
|
||||
* interruption occurs in a tightly controlled fashion.
|
||||
*
|
||||
* See SwingWorker.java for more details on its functionality and usage. It is
|
||||
* provided by Sun Microsystems for download and is not part of the Swing library.
|
||||
*/
|
||||
|
||||
class SimThread extends SwingWorker {
|
||||
private MIPSprogram p;
|
||||
private int pc, maxSteps;
|
||||
private int[] breakPoints;
|
||||
private boolean done;
|
||||
private ProcessingException pe;
|
||||
private volatile boolean stop = false;
|
||||
private volatile AbstractAction stopper;
|
||||
private AbstractAction starter;
|
||||
private int constructReturnReason;
|
||||
|
||||
|
||||
/**
|
||||
* SimThread constructor. Receives all the information it needs to simulate execution.
|
||||
*
|
||||
* @param p the MIPSprogram to be simulated
|
||||
* @param pc address in text segment of first instruction to simulate
|
||||
* @param maxSteps maximum number of instruction steps to simulate. Default of -1 means no maximum
|
||||
* @param breakPoints array of breakpoints (instruction addresses) specified by user
|
||||
* @param starter the GUI component responsible for this call, usually GO or STEP. null if none.
|
||||
*/
|
||||
SimThread(MIPSprogram p, int pc, int maxSteps, int[] breakPoints, AbstractAction starter) {
|
||||
super(Globals.getGui()!=null);
|
||||
this.p = p;
|
||||
this.pc = pc;
|
||||
this.maxSteps = maxSteps;
|
||||
this.breakPoints = breakPoints;
|
||||
this.done = false;
|
||||
this.pe = null;
|
||||
this.starter = starter;
|
||||
this.stopper = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets to "true" the volatile boolean variable that is tested after each
|
||||
* MIPS instruction is executed. After calling this method, the next test
|
||||
* will yield "true" and "construct" will return.
|
||||
*
|
||||
* @param actor the Swing component responsible for this call.
|
||||
*/
|
||||
public void setStop(AbstractAction actor) {
|
||||
stop = true;
|
||||
stopper = actor;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This is comparable to the Runnable "run" method (it is called by
|
||||
* SwingWorker's "run" method). It simulates the program
|
||||
* execution in the backgorund.
|
||||
*
|
||||
* @return boolean value true if execution done, false otherwise
|
||||
*/
|
||||
|
||||
public Object construct() {
|
||||
// The next two statements are necessary for GUI to be consistently updated
|
||||
// before the simulation gets underway. Without them, this happens only intermittently,
|
||||
// with a consequence that some simulations are interruptable using PAUSE/STOP and others
|
||||
// are not (because one or the other or both is not yet enabled).
|
||||
Thread.currentThread().setPriority(Thread.NORM_PRIORITY-1);
|
||||
Thread.yield(); // let the main thread run a bit to finish updating the GUI
|
||||
|
||||
if (breakPoints == null || breakPoints.length == 0) {
|
||||
breakPoints = null;
|
||||
}
|
||||
else {
|
||||
Arrays.sort(breakPoints); // must be pre-sorted for binary search
|
||||
}
|
||||
|
||||
Simulator.getInstance().notifyObserversOfExecutionStart(maxSteps, pc);
|
||||
|
||||
RegisterFile.initializeProgramCounter(pc);
|
||||
ProgramStatement statement = null;
|
||||
try {
|
||||
statement = Globals.memory.getStatement(RegisterFile.getProgramCounter());
|
||||
}
|
||||
catch (AddressErrorException e) {
|
||||
ErrorList el = new ErrorList();
|
||||
el.add(new ErrorMessage((MIPSprogram)null,0,0,"invalid program counter value: "+Binary.intToHexString(RegisterFile.getProgramCounter())));
|
||||
this.pe = new ProcessingException(el, e);
|
||||
// Next statement is a hack. Previous statement sets EPC register to ProgramCounter-4
|
||||
// because it assumes the bad address comes from an operand so the ProgramCounter has already been
|
||||
// incremented. In this case, bad address is the instruction fetch itself so Program Counter has
|
||||
// not yet been incremented. We'll set the EPC directly here. DPS 8-July-2013
|
||||
Coprocessor0.updateRegister(Coprocessor0.EPC, RegisterFile.getProgramCounter());
|
||||
this.constructReturnReason = EXCEPTION;
|
||||
this.done = true;
|
||||
SystemIO.resetFiles(); // close any files opened in MIPS program
|
||||
Simulator.getInstance().notifyObserversOfExecutionStop(maxSteps, pc);
|
||||
return new Boolean(done);
|
||||
}
|
||||
int steps = 0;
|
||||
|
||||
// ******************* PS addition 26 July 2006 **********************
|
||||
// A couple statements below were added for the purpose of assuring that when
|
||||
// "back stepping" is enabled, every instruction will have at least one entry
|
||||
// on the back-stepping stack. Most instructions will because they write either
|
||||
// to a register or memory. But "nop" and branches not taken do not. When the
|
||||
// user is stepping backward through the program, the stack is popped and if
|
||||
// an instruction has no entry it will be skipped over in the process. This has
|
||||
// no effect on the correctness of the mechanism but the visual jerkiness when
|
||||
// instruction highlighting skips such instrutions is disruptive. Current solution
|
||||
// is to add a "do nothing" stack entry for instructions that do no write anything.
|
||||
// To keep this invisible to the "simulate()" method writer, we
|
||||
// will push such an entry onto the stack here if there is none for this instruction
|
||||
// by the time it has completed simulating. This is done by the IF statement
|
||||
// just after the call to the simulate method itself. The BackStepper method does
|
||||
// the aforementioned check and decides whether to push or not. The result
|
||||
// is a a smoother interaction experience. But it comes at the cost of slowing
|
||||
// simulation speed for flat-out runs, for every MIPS instruction executed even
|
||||
// though very few will require the "do nothing" stack entry. For stepped or
|
||||
// timed execution the slower execution speed is not noticeable.
|
||||
//
|
||||
// To avoid this cost I tried a different technique: back-fill with "do nothings"
|
||||
// during the backstepping itself when this situation is recognized. Problem
|
||||
// was in recognizing all possible situations in which the stack contained such
|
||||
// a "gap". It became a morass of special cases and it seemed every weird test
|
||||
// case revealed another one. In addition, when a program
|
||||
// begins with one or more such instructions ("nop" and branches not taken),
|
||||
// the backstep button is not enabled until a "real" instruction is executed.
|
||||
// This is noticeable in stepped mode.
|
||||
// *********************************************************************
|
||||
|
||||
int pc = 0; // added: 7/26/06 (explanation above)
|
||||
|
||||
while (statement != null) {
|
||||
pc = RegisterFile.getProgramCounter(); // added: 7/26/06 (explanation above)
|
||||
RegisterFile.incrementPC();
|
||||
// Perform the MIPS instruction in synchronized block. If external threads agree
|
||||
// to access MIPS memory and registers only through synchronized blocks on same
|
||||
// lock variable, then full (albeit heavy-handed) protection of MIPS memory and
|
||||
// registers is assured. Not as critical for reading from those resources.
|
||||
synchronized (Globals.memoryAndRegistersLock) {
|
||||
try {
|
||||
if (Simulator.externalInterruptingDevice != NO_DEVICE) {
|
||||
int deviceInterruptCode = externalInterruptingDevice;
|
||||
Simulator.externalInterruptingDevice = NO_DEVICE;
|
||||
throw new ProcessingException(statement, "External Interrupt", deviceInterruptCode);
|
||||
}
|
||||
BasicInstruction instruction = (BasicInstruction)statement.getInstruction();
|
||||
if (instruction == null) {
|
||||
throw new ProcessingException(statement,
|
||||
"undefined instruction ("+Binary.intToHexString(statement.getBinaryStatement())+")",
|
||||
Exceptions.RESERVED_INSTRUCTION_EXCEPTION);
|
||||
}
|
||||
// THIS IS WHERE THE INSTRUCTION EXECUTION IS ACTUALLY SIMULATED!
|
||||
instruction.getSimulationCode().simulate(statement);
|
||||
|
||||
// IF statement added 7/26/06 (explanation above)
|
||||
if (Globals.getSettings().getBackSteppingEnabled()) {
|
||||
Globals.program.getBackStepper().addDoNothing(pc);
|
||||
}
|
||||
}
|
||||
catch (ProcessingException pe) {
|
||||
if (pe.errors() == null) {
|
||||
this.constructReturnReason = NORMAL_TERMINATION;
|
||||
this.done = true;
|
||||
SystemIO.resetFiles(); // close any files opened in MIPS program
|
||||
Simulator.getInstance().notifyObserversOfExecutionStop(maxSteps, pc);
|
||||
return new Boolean(done); // execution completed without error.
|
||||
}
|
||||
else {
|
||||
// See if an exception handler is present. Assume this is the case
|
||||
// if and only if memory location Memory.exceptionHandlerAddress
|
||||
// (e.g. 0x80000180) contains an instruction. If so, then set the
|
||||
// program counter there and continue. Otherwise terminate the
|
||||
// MIPS program with appropriate error message.
|
||||
ProgramStatement exceptionHandler = null;
|
||||
try {
|
||||
exceptionHandler = Globals.memory.getStatement(Memory.exceptionHandlerAddress);
|
||||
}
|
||||
catch (AddressErrorException aee) { } // will not occur with this well-known addres
|
||||
if (exceptionHandler != null) {
|
||||
RegisterFile.setProgramCounter(Memory.exceptionHandlerAddress);
|
||||
}
|
||||
else {
|
||||
this.constructReturnReason = EXCEPTION;
|
||||
this.pe = pe;
|
||||
this.done = true;
|
||||
SystemIO.resetFiles(); // close any files opened in MIPS program
|
||||
Simulator.getInstance().notifyObserversOfExecutionStop(maxSteps, pc);
|
||||
return new Boolean(done);
|
||||
}
|
||||
}
|
||||
}
|
||||
}// end synchronized block
|
||||
|
||||
///////// DPS 15 June 2007. Handle delayed branching if it occurs./////
|
||||
if (DelayedBranch.isTriggered()) {
|
||||
RegisterFile.setProgramCounter(DelayedBranch.getBranchTargetAddress());
|
||||
DelayedBranch.clear();
|
||||
}
|
||||
else if (DelayedBranch.isRegistered()) {
|
||||
DelayedBranch.trigger();
|
||||
}//////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Volatile variable initialized false but can be set true by the main thread.
|
||||
// Used to stop or pause a running MIPS program. See stopSimulation() above.
|
||||
if (stop == true) {
|
||||
this.constructReturnReason = PAUSE_OR_STOP;
|
||||
this.done = false;
|
||||
Simulator.getInstance().notifyObserversOfExecutionStop(maxSteps, pc);
|
||||
return new Boolean(done);
|
||||
}
|
||||
// Return if we've reached a breakpoint.
|
||||
if((breakPoints != null) &&
|
||||
(Arrays.binarySearch(breakPoints,RegisterFile.getProgramCounter()) >= 0)) {
|
||||
this.constructReturnReason = BREAKPOINT;
|
||||
this.done = false;
|
||||
Simulator.getInstance().notifyObserversOfExecutionStop(maxSteps, pc);
|
||||
return new Boolean(done); // false;
|
||||
}
|
||||
// Check number of MIPS instructions executed. Return if at limit (-1 is no limit).
|
||||
if (maxSteps > 0) {
|
||||
steps++;
|
||||
if (steps >= maxSteps) {
|
||||
this.constructReturnReason = MAX_STEPS;
|
||||
this.done = false;
|
||||
Simulator.getInstance().notifyObserversOfExecutionStop(maxSteps, pc);
|
||||
return new Boolean(done);// false;
|
||||
}
|
||||
}
|
||||
|
||||
// schedule GUI update only if: there is in fact a GUI! AND
|
||||
// using Run, not Step (maxSteps > 1) AND
|
||||
// running slowly enough for GUI to keep up
|
||||
//if (Globals.getGui() != null && maxSteps != 1 &&
|
||||
if (interactiveGUIUpdater != null && maxSteps != 1 &&
|
||||
RunSpeedPanel.getInstance().getRunSpeed() < RunSpeedPanel.UNLIMITED_SPEED) {
|
||||
SwingUtilities.invokeLater(interactiveGUIUpdater);
|
||||
}
|
||||
if (Globals.getGui() != null || Globals.runSpeedPanelExists) { // OR added by DPS 24 July 2008 to enable speed control by stand-alone tool
|
||||
if (maxSteps != 1 &&
|
||||
RunSpeedPanel.getInstance().getRunSpeed() < RunSpeedPanel.UNLIMITED_SPEED) {
|
||||
try { Thread.sleep((int)(1000/RunSpeedPanel.getInstance().getRunSpeed())); // make sure it's never zero!
|
||||
}
|
||||
catch (InterruptedException e) {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Get next instruction in preparation for next iteration.
|
||||
|
||||
try {
|
||||
statement = Globals.memory.getStatement(RegisterFile.getProgramCounter());
|
||||
}
|
||||
catch (AddressErrorException e) {
|
||||
ErrorList el = new ErrorList();
|
||||
el.add(new ErrorMessage((MIPSprogram)null,0,0,"invalid program counter value: "+Binary.intToHexString(RegisterFile.getProgramCounter())));
|
||||
this.pe = new ProcessingException(el,e);
|
||||
// Next statement is a hack. Previous statement sets EPC register to ProgramCounter-4
|
||||
// because it assumes the bad address comes from an operand so the ProgramCounter has already been
|
||||
// incremented. In this case, bad address is the instruction fetch itself so Program Counter has
|
||||
// not yet been incremented. We'll set the EPC directly here. DPS 8-July-2013
|
||||
Coprocessor0.updateRegister(Coprocessor0.EPC, RegisterFile.getProgramCounter());
|
||||
this.constructReturnReason = EXCEPTION;
|
||||
this.done = true;
|
||||
SystemIO.resetFiles(); // close any files opened in MIPS program
|
||||
Simulator.getInstance().notifyObserversOfExecutionStop(maxSteps, pc);
|
||||
return new Boolean(done);
|
||||
}
|
||||
}
|
||||
// DPS July 2007. This "if" statement is needed for correct program
|
||||
// termination if delayed branching on and last statement in
|
||||
// program is a branch/jump. Program will terminate rather than branch,
|
||||
// because that's what MARS does when execution drops off the bottom.
|
||||
if (DelayedBranch.isTriggered() || DelayedBranch.isRegistered()) {
|
||||
DelayedBranch.clear();
|
||||
}
|
||||
// If we got here it was due to null statement, which means program
|
||||
// counter "fell off the end" of the program. NOTE: Assumes the
|
||||
// "while" loop contains no "break;" statements.
|
||||
this.constructReturnReason = CLIFF_TERMINATION;
|
||||
this.done = true;
|
||||
SystemIO.resetFiles(); // close any files opened in MIPS program
|
||||
Simulator.getInstance().notifyObserversOfExecutionStop(maxSteps, pc);
|
||||
return new Boolean(done); // true; // execution completed
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method is invoked by the SwingWorker when the "construct" method returns.
|
||||
* It will update the GUI appropriately. According to Sun's documentation, it
|
||||
* is run in the main thread so should work OK with Swing components (which are
|
||||
* not thread-safe).
|
||||
*
|
||||
* Its action depends on what caused the return from construct() and what
|
||||
* action led to the call of construct() in the first place.
|
||||
*/
|
||||
|
||||
public void finished() {
|
||||
// If running from the command-line, then there is no GUI to update.
|
||||
if (Globals.getGui() == null) {
|
||||
return;
|
||||
}
|
||||
String starterName = (String) starter.getValue(AbstractAction.NAME);
|
||||
if (starterName.equals("Step")) {
|
||||
((RunStepAction)starter).stepped(done,constructReturnReason,pe);
|
||||
}
|
||||
if (starterName.equals("Go")) {
|
||||
if (done) {
|
||||
((RunGoAction)starter).stopped(pe,constructReturnReason);
|
||||
}
|
||||
else if (constructReturnReason == BREAKPOINT) {
|
||||
((RunGoAction)starter).paused(done,constructReturnReason,pe);
|
||||
}
|
||||
else {
|
||||
String stopperName = (String) stopper.getValue(AbstractAction.NAME);
|
||||
if ("Pause".equals(stopperName)) {
|
||||
((RunGoAction)starter).paused(done,constructReturnReason,pe);
|
||||
}
|
||||
else if ("Stop".equals(stopperName)) {
|
||||
((RunGoAction)starter).stopped(pe,constructReturnReason);
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class UpdateGUI implements Runnable {
|
||||
public void run() {
|
||||
if (Globals.getGui().getRegistersPane().getSelectedComponent() ==
|
||||
Globals.getGui().getMainPane().getExecutePane().getRegistersWindow()) {
|
||||
Globals.getGui().getMainPane().getExecutePane().getRegistersWindow().updateRegisters();
|
||||
}
|
||||
else {
|
||||
Globals.getGui().getMainPane().getExecutePane().getCoprocessor1Window().updateRegisters();
|
||||
}
|
||||
Globals.getGui().getMainPane().getExecutePane().getDataSegmentWindow().updateValues();
|
||||
Globals.getGui().getMainPane().getExecutePane().getTextSegmentWindow().setCodeHighlighting(true);
|
||||
Globals.getGui().getMainPane().getExecutePane().getTextSegmentWindow().highlightStepAtPC();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,82 @@
|
||||
package mars.simulator;
|
||||
|
||||
/*
|
||||
Copyright (c) 2003-2009, Pete Sanderson and Kenneth Vollmar
|
||||
|
||||
Developed by Pete Sanderson (psanderson@otterbein.edu)
|
||||
and Kenneth Vollmar (kenvollmar@missouristate.edu)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject
|
||||
to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
(MIT license, http://www.opensource.org/licenses/mit-license.html)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Object provided to Observers of the Simulator.
|
||||
* They are notified at important phases of the runtime simulator,
|
||||
* such as start and stop of simulation.
|
||||
*
|
||||
* @author Pete Sanderson
|
||||
* @version January 2009
|
||||
*/
|
||||
|
||||
public class SimulatorNotice {
|
||||
private int action;
|
||||
private int maxSteps;
|
||||
private double runSpeed;
|
||||
private int programCounter;
|
||||
public static final int SIMULATOR_START = 0;
|
||||
public static final int SIMULATOR_STOP = 1;
|
||||
|
||||
/** Constructor will be called only within this package, so assume
|
||||
* address and length are in valid ranges.
|
||||
*/
|
||||
public SimulatorNotice(int action, int maxSteps, double runSpeed, int programCounter) {
|
||||
this.action = action;
|
||||
this.maxSteps = maxSteps;
|
||||
this.runSpeed = runSpeed;
|
||||
this.programCounter = programCounter;
|
||||
}
|
||||
|
||||
/** Fetch the memory address that was accessed. */
|
||||
public int getAction() {
|
||||
return this.action;
|
||||
}
|
||||
/** Fetch the length in bytes of the access operation (4,2,1). */
|
||||
public int getMaxSteps() {
|
||||
return this.maxSteps;
|
||||
}
|
||||
/** Fetch the value of the access operation (the value read or written). */
|
||||
public double getRunSpeed() {
|
||||
return this.runSpeed;
|
||||
}
|
||||
|
||||
/** Fetch the value of the access operation (the value read or written). */
|
||||
public int getProgramCounter() {
|
||||
return this.programCounter;
|
||||
}
|
||||
/** String representation indicates access type, address and length in bytes */
|
||||
public String toString() {
|
||||
return ((this.getAction()==SIMULATOR_START) ? "START " : "STOP ") +
|
||||
"Max Steps " + this.maxSteps + " " +
|
||||
"Speed "+ ((this.runSpeed==mars.venus.RunSpeedPanel.UNLIMITED_SPEED)? "unlimited " : ""+this.runSpeed+" inst/sec") +
|
||||
"Prog Ctr "+this.programCounter;
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,143 @@
|
||||
package mars.simulator;
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
/*-----------------------------------------------------
|
||||
* This file downloaded from the Sun Microsystems URL given below.
|
||||
*
|
||||
* I will subclass it to create worker thread for running
|
||||
* MIPS simulated execution.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This is the 3rd version of SwingWorker (also known as
|
||||
* SwingWorker 3), an abstract class that you subclass to
|
||||
* perform GUI-related work in a dedicated thread. For
|
||||
* instructions on and examples of using this class, see:
|
||||
*
|
||||
* http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html
|
||||
*
|
||||
* Note that the API changed slightly in the 3rd version:
|
||||
* You must now invoke start() on the SwingWorker after
|
||||
* creating it.
|
||||
*/
|
||||
public abstract class SwingWorker {
|
||||
private Object value; // see getValue(), setValue()
|
||||
|
||||
/**
|
||||
* Class to maintain reference to current worker thread
|
||||
* under separate synchronization control.
|
||||
*/
|
||||
private static class ThreadVar {
|
||||
private Thread thread;
|
||||
ThreadVar(Thread t) { thread = t; }
|
||||
synchronized Thread get() { return thread; }
|
||||
synchronized void clear() { thread = null; }
|
||||
}
|
||||
|
||||
private ThreadVar threadVar;
|
||||
|
||||
/**
|
||||
* Get the value produced by the worker thread, or null if it
|
||||
* hasn't been constructed yet.
|
||||
*/
|
||||
protected synchronized Object getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value produced by worker thread
|
||||
*/
|
||||
private synchronized void setValue(Object x) {
|
||||
value = x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the value to be returned by the <code>get</code> method.
|
||||
*/
|
||||
public abstract Object construct();
|
||||
|
||||
/**
|
||||
* Called on the event dispatching thread (not on the worker thread)
|
||||
* after the <code>construct</code> method has returned.
|
||||
*/
|
||||
public void finished() {
|
||||
}
|
||||
|
||||
/**
|
||||
* A new method that interrupts the worker thread. Call this method
|
||||
* to force the worker to stop what it's doing.
|
||||
*/
|
||||
public void interrupt() {
|
||||
Thread t = threadVar.get();
|
||||
if (t != null) {
|
||||
t.interrupt();
|
||||
}
|
||||
threadVar.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value created by the <code>construct</code> method.
|
||||
* Returns null if either the constructing thread or the current
|
||||
* thread was interrupted before a value was produced.
|
||||
*
|
||||
* @return the value created by the <code>construct</code> method
|
||||
*/
|
||||
public Object get() {
|
||||
while (true) {
|
||||
Thread t = threadVar.get();
|
||||
if (t == null) {
|
||||
return getValue();
|
||||
}
|
||||
try {
|
||||
t.join();
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt(); // propagate
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Start a thread that will call the <code>construct</code> method
|
||||
* and then exit.
|
||||
* @param useSwing Set true if MARS is running from GUI, false otherwise.
|
||||
*/
|
||||
public SwingWorker(final boolean useSwing) {
|
||||
final Runnable doFinished = new Runnable() {
|
||||
public void run() { finished(); }
|
||||
};
|
||||
|
||||
Runnable doConstruct = new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
setValue(construct());
|
||||
}
|
||||
finally {
|
||||
threadVar.clear();
|
||||
}
|
||||
|
||||
if (useSwing) SwingUtilities.invokeLater(doFinished);
|
||||
else doFinished.run();
|
||||
}
|
||||
};
|
||||
|
||||
// Thread that represents executing MIPS program...
|
||||
Thread t = new Thread(doConstruct, "MIPS");
|
||||
|
||||
//t.setPriority(Thread.NORM_PRIORITY-1);//******************
|
||||
|
||||
threadVar = new ThreadVar(t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the worker thread.
|
||||
*/
|
||||
public void start() {
|
||||
Thread t = threadVar.get();
|
||||
if (t != null) {
|
||||
t.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user