MIPS ASSEMBLER AND SIMULATOR

Developed by Steve Lewis

Copyright (C) 2001 University of Florida

All rights reserved.

This document describes the Java implementation of the MIPS assembler and simulator API (Application Programming Interface).

I. THE MIPS ASSEMBLER

======

The MIPSASM.java source file contains an example on how to use the MIPS assembler. That is, MIPSASM is a default assembler implementation that outputs to the standard output. This output can be redirected to a file. However, any user interface for the MIPS simulator should integrate both the MIPS assembler and simulator seamlessly.

Basically, code for the assembly process goes like this:

Assembler a = new Assembler();

// PHASE 1

Vector v = Utility.vLoadFileAsVector("program.s");

Enumeration e = v.elements();

int iLine = 0;

while (e.hasMoreElements()) {

iLine++;

if (a.iProcessLine((String)e.nextElement(), iLine) != 0) {

System.out.println("Phase 1 Error");

System.exit(-1);

}

}

// PHASE 2

StringBuffer sb = a.sbGetMachineCode();

if (a.iGetNumPhaseTwoErrors() != 0) {

System.out.println("Phase 2 Error");

System.exit(-1);

}

... We can now use the machine code version of the

... program that is stored in StringBuffer sb.

... For example, to process each line in the machine code:

StringTokenizer st = new StringTokenizer(sb.toString(), "\n");

while (st.hasMoreTokens()) {

System.out.println(st.nextToken());

// or: simulator.iLoadCodeString(st.nextToken());

// or: commandProcess.processCommand(“LOAD “ + st.nextToken());

}

The API for the Assembler class is as follows:

public Assembler()

Default constructor.

public void reset()

Reset the assembler. Called automatically by the constructor.

public int iProcessLine(String sInput, int iCurrLine)

Processes a given line from the program source. iCurrLine should be the corresponding line number from the source file (or 0 if unknown or unimportant).

RETURNS

0 = SUCCESS

-1 = Colon error

-2 = Label already defined

-3 = Invalid mode while adding a label.

-4 = Directive error

-5 = Text error

public StringBuffer sbGetMachineCode(String sSourceName)

After processing all the lines of the source file, this method returns the machine code version of the program. The contents of the StringBuffer returned can be written to disk directly, or manipulated in memory. Each line of the machine code is divided by a carriage return ('\n' character). To determine if there are errors, you should use iGetNumPhaseTwoErrors()

public int iGetLastInvalidUseOfAT()

Returns the line containing the last invalid use of the $at register (or -1 if there was none).

public int iGetNumPhaseTwoErrors()

Returns the number of errors encountered during the second pass. This could include undefined labels, etc.

public String toString()

Outputs the assembled program with information useful for debugging.

public boolean bLabelDefined(String s)

Searches for label s and returns TRUE if it has been defined yet, otherwise returns FALSE.

public int iGetLabelAddress(String s)

Returns the memory address associated with label s. Technically, if label s is undefined, this method will return 0. However, it might be the case that label s really is defined at address 0. Therefore, you should first use the bLabelDefined(String) method above to make sure the label is actually defined.

II. THE MIPS SIMULATOR

======

Interface Layers for the MIPS Simulator

+------+

: SimMIPS : [1] UI Layer

+------+------+

: CommandProcessor : CommandResultBuffer : [2] Command

+------+------+ Layer

: Command :

+------+

: ADD CHECKPOINT EXIT GET HELP HISTORY INPUT LIST LOAD :

: OUTPUT REMOVE RESET RUN SET STATUS STEP UNDO :

+------+

: Simulator : [3] Sim Layer

+------+

[1] UI Layer: This is the user interface layer. SimMIPS.java is the default command line user interface for the MIPS simulator. This top layer is designed to be readily replaced by any other user interface implementation. This is accomplished by requiring the user interface to access the simulator through the Command layer.

[2] Command Layer: This layer is an intermediate layer between the simulator and the simulators user interface. Its purpose is to enforce consistency amongst all user interfaces implemented for the simulator.

[3] Sim Layer: The Sim Layer represents the simulator interface. In reality, the simulator consists of several deeper layers.

The user interface may choose to bypass the Command Layer, and access the Sim Layer directly. While this would improve performance, doing so would restrict the user interfaces' ability to function with newer versions of the simulator. Having the Command Layer promotes decoupling between the simulators user interface and the simulator itself.

Therefore, anyone implementing the user interface for this simulator has two choices: (1) Use the CommandProcessor to interface with the simulator indirectly, (2) interface with the simulator directly. The choice will depend on the requirements of the user interface, but the first choice is strongly encouraged.

How to approach either option is described below.

OPTION 1: Interfacing with the CommandProcessor

------

SimMIPS.java is a basic command-line based user interface. It was used for the purpose of testing during the development of the simulator. It should also be used as an example of how to use the CommandProcessor and CommandResultBuffer to communicate with the simulator.

There are essentially three steps required by the user interface:

1. Create and maintain an instance of the class CommandProcessor.

2. Issue commands by using the processCommand(String) method of this CommandProcessor instance.

3. Process command responses by using the sGetNextResponse() of the CommandResultBuffer instance returned by the preceeding call to processCommand(String).

However, there are a few important details.

a. After step 1 above, a "RESET" command should be issued immediately to initialize the simulator. The response of this command should be examined to ensure that the simulator was initialized without error.

b. Before terminating the user interface program, an "EXIT" command should be issued. This ensures that the simulator is terminated gracefully. As above, you should verify the result of the "EXIT" command. You can also verify that the simulator is terminated by using the bSimulatorAllocated() method of the CommandProcessor instance.

c. Certain commands, such as "OUTPUT", return additional information that is not suitable for the CommandResultBuffer. For such a command, one can use the getLastCommand() method of the CommandProcessor instance. The command instance returned will contain the necessary additional information.

The CommandProcessor supports a limited set of commands. Refer to the document “MIPS SIMULATOR COMMAND PROTOCOL SPECIFICATION” for a reference that describes the protocol for these commands, their parameters, and the format of the command responses.

For reference, the API for the CommandProcessor is described below. The command protocol for the CommandProcessor may be extended in the future, but will always retain compatibility with the current protocol (thus the user interface will work with any future release of the simulator):

public CommandResultBuffer processCommand(String sCommandLine)

All commands are issued using this method. The string parameter sCommandLine contains the command to be issued as a literal string.

e.g. crb = processor.processCommand("GET MEMORY");

To process the command responses, use the sGetNextResponse() method of

the CommandResultBuffer class (e.g. String s = crb.sGetNextResponse();).

public Command getLastCommand() {

Returns a reference to the last Command instance that was issued. Certain commands (such as OUTPUT) contain information that the client might want to use.

public long lGetCurrentCycle()

Returns the current cycle count of the simulator. Essentially, it represents the number of executed instructions.

public void initializeSimulator()

This method is called when a RESET command is issued.

public void terminateSimulator()

This method is called when the EXIT command is issued.

public boolean bSimulatorAllocated()

Returns TRUE if the simulator has been allocated. Otherwise, returns FALSE.

OPTION 2: Interfacing with the Simulator

------

The Command Layer consists of several sub-layers. The design of the CommandProcessor is such that each command is implemented as a separate object.

By examining the source code contained in each CommandXXX.java file, you can get an idea of how to interface with the Simulator layer directly. For example, by looking at CommandStep.java, you can see the steps necessary to instruct the simulator to perform one cycle of execution (and also handle any potentially errors). Looking at these source files as examples is probably the best way to understand how to interface with the simulator directly.

For reference, the API for the CommandProcessor is described below. Keep in mind that this is subject to change in new releases of the simulator:

public static final int EXCEPTION_NONE = -1

public static final int EXCEPTION_FETCH_ERROR = 0

public static final int EXCEPTION_DECODE_ERROR = 1

public static final int EXCEPTION_EXECUTE_ERROR = 2

public static final int EXCEPTION_FP_REQUIRED = 3

public static final int EXCEPTION_OUTPUT_WAITING = 4

public static final int EXCEPTION_INPUT_WAITING = 5

public static final int EXCEPTION_EXIT = 6

public static final int EXCEPTION_INVALID_OPCODE = 7

public static final int EXCEPTION_SYSCALL = 8

public static final int EXCEPTION_BREAK = 9

public static final int EXCEPTION_OVERFLOW = 10

public static final int EXCEPTION_FP_ERROR = 11

The iGetExceptionCode() method will return one of these constants.

public static final int NO_GUARD = -1

public static final int NO_BREAKPOINT = -1

Possibly returned by a call to either iGetGuardIndex() or iGetBreakpointIndex()

public Simulator(boolean bHistoryRecordingEnabled,

int iCheckpointCreationFactor)

Primary constructor, used to manually set two internal state variables.

public Simulator()

Default constructor.

public void reset()

Reset the simulator back to the startup state. The simulator is automatically reset when constructed.

public int iLoadCodeString(String sInput)

Used to load a program into memory. This method should be applied to each line of a code file that is to be loaded.

RETURNS

0 = SUCCESS

-1 = INVALID_LINE_IDENTIFIER_TOKEN

-2 = CRITICAL_ERROR (some exception, such as out of memory)

public int iPerformStep(long lStepDistance) throws SimulatorException

Use this method to issue lStepDistance number of cycles. The SimulatorException will be thrown if there is a CPU exception, or a simulator error.

RETURNS

0 = SUCCESS

-1 = BREAKPOINT ENCOUNTERED (which one is set in iBreakpointIndex)

-2 = GUARD ENCOUNTED (which one is set in iGuardIndex)

-3 = CRITICAL ERROR (out of memory)

public int iPerformUndo(long lUndoDistance)

Use this method to undo the past lUndoDistance number of cycles. This only works if history recording is enabled.

RETURNS

0 = SUCCESS

-1 = NO MORE STATE HISTORY CHANGE

-2 = ERROR APPLYING STATE HISTORY CHANGE

-3 = ERROR APPLYING CHECKPOINT STATE

-4 = ERROR RE-EXECUTING FROM LAST CHECKPOINT

-5 = EXCEPTION ENCOUNTED WHILE UNDO FROM CHECKPOINT

-6 = OUTPUT ERROR WHILE RE-EXECUTING INSTRUCTIONS

public void makeCheckpoint()

Force the creation of a checkpoint.

public int iGetNextCheckpointByteSize()

Gets the estimated size of the next checkpoint (in bytes).

public int iGetNumCheckpoints()

Returns the number of internally defined checkpoints.

public int iGetNumStateChanges()

Returns the estimated size (number of elements) of the state change record.

public int iGetStateHistoryByteSize()

Returns the size (number of bytes) of the state history.

public BreakpointList getBreakpointList()

Returns an instance of BreakpointList that internally holds a list of all the currently defined breakpoints.

public GuardList getGuardList()

Returns an instance of GuardList that internally holds a list of all the currently defined guards.

public Vector vGetStateHistory()

Returns a vector of all the state change records.

public Vector vGetCheckpoint()

Returns a vector of all the current checkpoints.

public State getState()

This method is public only for performance reasons. For certain commands (STATUS, GET, HISTORY, SET), it helps to have direct access to the State Layer. State refers to state variables such as registers, memory, etc.

public int iGetBreakpointIndex()

Returns which breakpoint is currently encountered.

(returns NO_BREAKPOINT if none)

public int iGetGuardIndex()

Returns which guard is currently encountered.

(returns NO_GUARD if none)

public long lGetCycleIndex()

Returns the current cycle index.

public int iGetExceptionCode()

Returns the current exception code, caused by some exception during the last cycle. Refer to the EXCEPTION_XXX constants above.

public void writeHistoryLog(String sFilename)

Write the entire contents of the current history log to the file sFilename.

public StringBuffer sbGetHistoryLog()

Returns the entire contents of the current history log in an instance of StringBuffer. This can easily cause an OutOfMemoryException.

public StringBuffer sbGetOutputFromUndo()

If re-execution occurred while performing an undo, this method returns any output that was re-sent in the process.

public boolean bHasFPA()

Returns TRUE if the FPA (Floating Point Arithmetic) coprocessor is enabled. It is toggled on and off by a CP0 control register.

public int iWriteInput(String sInput)

Sends the input string sInput into the simulator.

RETURNS

0 = SUCCESS

-1 = ERR_NO_INPUT_EXPECTED (input discarded)

-2 = ERR_INPUT_FORMAT (e.g. specifying a string when integer is expected)

public int iReadOutput(StringBuffer sBuffer)

Receives any pending output from the simulator. Before calling this method, the parameter sBuffer must already be initialized as an instance of StringBuffer.

RETURNS

0 = SUCESS

-1 = ERR_NO_OUTPUT_EXPECTED

-2 = ERR_OUTPUT_FORMAT (out of memory?)

-3 = ERR_NO_STRING_BUFFER