Source code of MARS Assembler

First commit of the 4.5 version (latest version available)
This commit is contained in:
adolphenom
2014-12-21 12:49:28 +01:00
parent 343642d6ad
commit 0da1c5dcca
1333 changed files with 207089 additions and 3 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+460
View File
@@ -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.
+159
View File
@@ -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.
+97
View File
@@ -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.
+207
View File
@@ -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.
+536
View File
@@ -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.
+82
View File
@@ -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.
+143
View File
@@ -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();
}
}
}